]> git.lizzy.rs Git - minetest.git/blob - src/tile.cpp
All textures are are now searched first from the directory specified by the texture_p...
[minetest.git] / src / tile.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "tile.h"
21 #include "debug.h"
22 #include "main.h" // for g_settings
23 #include "filesys.h"
24 #include "utility.h"
25
26 /*
27         A cache from texture name to texture path
28 */
29 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
30
31 /*
32         Replaces the filename extension.
33         eg:
34                 std::string image = "a/image.png"
35                 replace_ext(image, "jpg")
36                 -> image = "a/image.jpg"
37         Returns true on success.
38 */
39 static bool replace_ext(std::string &path, const char *ext)
40 {
41         if(ext == NULL)
42                 return false;
43         // Find place of last dot, fail if \ or / found.
44         s32 last_dot_i = -1;
45         for(s32 i=path.size()-1; i>=0; i--)
46         {
47                 if(path[i] == '.')
48                 {
49                         last_dot_i = i;
50                         break;
51                 }
52                 
53                 if(path[i] == '\\' || path[i] == '/')
54                         break;
55         }
56         // If not found, return an empty string
57         if(last_dot_i == -1)
58                 return false;
59         // Else make the new path
60         path = path.substr(0, last_dot_i+1) + ext;
61         return true;
62 }
63
64 /*
65         Find out the full path of an image by trying different filename
66         extensions.
67
68         If failed, return "".
69 */
70 static std::string getImagePath(std::string path)
71 {
72         // A NULL-ended list of possible image extensions
73         const char *extensions[] = {
74                 "png", "jpg", "bmp", "tga",
75                 "pcx", "ppm", "psd", "wal", "rgb",
76                 NULL
77         };
78
79         const char **ext = extensions;
80         do{
81                 bool r = replace_ext(path, *ext);
82                 if(r == false)
83                         return "";
84                 if(fs::PathExists(path))
85                         return path;
86         }
87         while((++ext) != NULL);
88         
89         return "";
90 }
91
92 /*
93         Gets the path to a texture by first checking if the texture exists
94         in texture_path and if not, using the data path.
95
96         Checks all supported extensions by replacing the original extension.
97
98         If not found, returns "".
99
100         Utilizes a thread-safe cache.
101 */
102 std::string getTexturePath(const std::string &filename)
103 {
104         std::string fullpath = "";
105         /*
106                 Check from cache
107         */
108         bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
109         if(incache)
110                 return fullpath;
111         
112         /*
113                 Check from texture_path
114         */
115         std::string texture_path = g_settings.get("texture_path");
116         if(texture_path != "")
117         {
118                 std::string testpath = texture_path + '/' + filename;
119                 // Check all filename extensions. Returns "" if not found.
120                 fullpath = getImagePath(testpath);
121         }
122         
123         /*
124                 Check from default data directory
125         */
126         if(fullpath == "")
127         {
128                 std::string testpath = porting::getDataPath(filename.c_str());
129                 // Check all filename extensions. Returns "" if not found.
130                 fullpath = getImagePath(testpath);
131         }
132         
133         // Add to cache (also an empty result is cached)
134         g_texturename_to_path_cache.set(filename, fullpath);
135         
136         // Finally return it
137         return fullpath;
138 }
139
140 /*
141         TextureSource
142 */
143
144 TextureSource::TextureSource(IrrlichtDevice *device):
145                 m_device(device),
146                 m_main_atlas_image(NULL),
147                 m_main_atlas_texture(NULL)
148 {
149         assert(m_device);
150         
151         m_atlaspointer_cache_mutex.Init();
152         
153         m_main_thread = get_current_thread_id();
154         
155         // Add a NULL AtlasPointer as the first index, named ""
156         m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
157         m_name_to_id[""] = 0;
158
159         // Build main texture atlas
160         if(g_settings.getBool("enable_texture_atlas"))
161                 buildMainAtlas();
162         else
163                 dstream<<"INFO: Not building texture atlas."<<std::endl;
164 }
165
166 TextureSource::~TextureSource()
167 {
168 }
169
170 void TextureSource::processQueue()
171 {
172         /*
173                 Fetch textures
174         */
175         if(m_get_texture_queue.size() > 0)
176         {
177                 GetRequest<std::string, u32, u8, u8>
178                                 request = m_get_texture_queue.pop();
179
180                 dstream<<"INFO: TextureSource::processQueue(): "
181                                 <<"got texture request with "
182                                 <<"name="<<request.key
183                                 <<std::endl;
184
185                 GetResult<std::string, u32, u8, u8>
186                                 result;
187                 result.key = request.key;
188                 result.callers = request.callers;
189                 result.item = getTextureIdDirect(request.key);
190
191                 request.dest->push_back(result);
192         }
193 }
194
195 u32 TextureSource::getTextureId(const std::string &name)
196 {
197         //dstream<<"INFO: getTextureId(): name="<<name<<std::endl;
198
199         {
200                 /*
201                         See if texture already exists
202                 */
203                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
204                 core::map<std::string, u32>::Node *n;
205                 n = m_name_to_id.find(name);
206                 if(n != NULL)
207                 {
208                         return n->getValue();
209                 }
210         }
211         
212         /*
213                 Get texture
214         */
215         if(get_current_thread_id() == m_main_thread)
216         {
217                 return getTextureIdDirect(name);
218         }
219         else
220         {
221                 dstream<<"INFO: getTextureId(): Queued: name="<<name<<std::endl;
222
223                 // We're gonna ask the result to be put into here
224                 ResultQueue<std::string, u32, u8, u8> result_queue;
225                 
226                 // Throw a request in
227                 m_get_texture_queue.add(name, 0, 0, &result_queue);
228                 
229                 dstream<<"INFO: Waiting for texture from main thread, name="
230                                 <<name<<std::endl;
231                 
232                 try
233                 {
234                         // Wait result for a second
235                         GetResult<std::string, u32, u8, u8>
236                                         result = result_queue.pop_front(1000);
237                 
238                         // Check that at least something worked OK
239                         assert(result.key == name);
240
241                         return result.item;
242                 }
243                 catch(ItemNotFoundException &e)
244                 {
245                         dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
246                         return 0;
247                 }
248         }
249         
250         dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
251
252         return 0;
253 }
254
255 // Draw a progress bar on the image
256 void make_progressbar(float value, video::IImage *image);
257
258 /*
259         Generate image based on a string like "stone.png" or "[crack0".
260         if baseimg is NULL, it is created. Otherwise stuff is made on it.
261 */
262 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
263                 IrrlichtDevice *device);
264
265 /*
266         Generates an image from a full string like
267         "stone.png^mineral_coal.png^[crack0".
268
269         This is used by buildMainAtlas().
270 */
271 video::IImage* generate_image_from_scratch(std::string name,
272                 IrrlichtDevice *device);
273
274 /*
275         This method generates all the textures
276 */
277 u32 TextureSource::getTextureIdDirect(const std::string &name)
278 {
279         dstream<<"INFO: getTextureIdDirect(): name="<<name<<std::endl;
280
281         // Empty name means texture 0
282         if(name == "")
283         {
284                 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
285                 return 0;
286         }
287         
288         /*
289                 Calling only allowed from main thread
290         */
291         if(get_current_thread_id() != m_main_thread)
292         {
293                 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
294                                 "called not from main thread"<<std::endl;
295                 return 0;
296         }
297
298         /*
299                 See if texture already exists
300         */
301         {
302                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
303
304                 core::map<std::string, u32>::Node *n;
305                 n = m_name_to_id.find(name);
306                 if(n != NULL)
307                 {
308                         dstream<<"INFO: getTextureIdDirect(): name="<<name
309                                         <<" found in cache"<<std::endl;
310                         return n->getValue();
311                 }
312         }
313
314         dstream<<"INFO: getTextureIdDirect(): name="<<name
315                         <<" NOT found in cache. Creating it."<<std::endl;
316         
317         /*
318                 Get the base image
319         */
320
321         char separator = '^';
322
323         /*
324                 This is set to the id of the base image.
325                 If left 0, there is no base image and a completely new image
326                 is made.
327         */
328         u32 base_image_id = 0;
329         
330         // Find last meta separator in name
331         s32 last_separator_position = -1;
332         for(s32 i=name.size()-1; i>=0; i--)
333         {
334                 if(name[i] == separator)
335                 {
336                         last_separator_position = i;
337                         break;
338                 }
339         }
340         /*
341                 If separator was found, construct the base name and make the
342                 base image using a recursive call
343         */
344         std::string base_image_name;
345         if(last_separator_position != -1)
346         {
347                 // Construct base name
348                 base_image_name = name.substr(0, last_separator_position);
349                 dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
350                                 " to get base image, name="<<base_image_name<<std::endl;
351                 base_image_id = getTextureIdDirect(base_image_name);
352         }
353         
354         dstream<<"base_image_id="<<base_image_id<<std::endl;
355         
356         video::IVideoDriver* driver = m_device->getVideoDriver();
357         assert(driver);
358
359         video::ITexture *t = NULL;
360
361         /*
362                 An image will be built from files and then converted into a texture.
363         */
364         video::IImage *baseimg = NULL;
365         
366         // If a base image was found, copy it to baseimg
367         if(base_image_id != 0)
368         {
369                 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
370
371                 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
372
373                 video::IImage *image = ap.atlas_img;
374                 
375                 if(image == NULL)
376                 {
377                         dstream<<"WARNING: getTextureIdDirect(): NULL image in "
378                                         <<"cache: \""<<base_image_name<<"\""
379                                         <<std::endl;
380                 }
381                 else
382                 {
383                         core::dimension2d<u32> dim = ap.intsize;
384
385                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
386
387                         core::position2d<s32> pos_to(0,0);
388                         core::position2d<s32> pos_from = ap.intpos;
389                         
390                         image->copyTo(
391                                         baseimg, // target
392                                         v2s32(0,0), // position in target
393                                         core::rect<s32>(pos_from, dim) // from
394                         );
395
396                         dstream<<"INFO: getTextureIdDirect(): Loaded \""
397                                         <<base_image_name<<"\" from image cache"
398                                         <<std::endl;
399                 }
400         }
401         
402         /*
403                 Parse out the last part of the name of the image and act
404                 according to it
405         */
406
407         std::string last_part_of_name = name.substr(last_separator_position+1);
408         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
409
410         // Generate image according to part of name
411         if(generate_image(last_part_of_name, baseimg, m_device) == false)
412         {
413                 dstream<<"INFO: getTextureIdDirect(): "
414                                 "failed to generate \""<<last_part_of_name<<"\""
415                                 <<std::endl;
416         }
417
418         // If no resulting image, print a warning
419         if(baseimg == NULL)
420         {
421                 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
422                                 " create texture \""<<name<<"\""<<std::endl;
423         }
424         
425         if(baseimg != NULL)
426         {
427                 // Create texture from resulting image
428                 t = driver->addTexture(name.c_str(), baseimg);
429         }
430         
431         /*
432                 Add texture to caches (add NULL textures too)
433         */
434
435         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
436         
437         u32 id = m_atlaspointer_cache.size();
438         AtlasPointer ap(id);
439         ap.atlas = t;
440         ap.pos = v2f(0,0);
441         ap.size = v2f(1,1);
442         ap.tiled = 0;
443         core::dimension2d<u32> baseimg_dim(0,0);
444         if(baseimg)
445                 baseimg_dim = baseimg->getDimension();
446         SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
447         m_atlaspointer_cache.push_back(nap);
448         m_name_to_id.insert(name, id);
449
450         dstream<<"INFO: getTextureIdDirect(): name="<<name
451                         <<": succesfully returning id="<<id<<std::endl;
452         
453         return id;
454 }
455
456 std::string TextureSource::getTextureName(u32 id)
457 {
458         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
459
460         if(id >= m_atlaspointer_cache.size())
461         {
462                 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
463                                 <<" >= m_atlaspointer_cache.size()="
464                                 <<m_atlaspointer_cache.size()<<std::endl;
465                 return "";
466         }
467         
468         return m_atlaspointer_cache[id].name;
469 }
470
471
472 AtlasPointer TextureSource::getTexture(u32 id)
473 {
474         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
475
476         if(id >= m_atlaspointer_cache.size())
477                 return AtlasPointer(0, NULL);
478         
479         return m_atlaspointer_cache[id].a;
480 }
481
482 void TextureSource::buildMainAtlas() 
483 {
484         dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
485
486         //return; // Disable (for testing)
487         
488         video::IVideoDriver* driver = m_device->getVideoDriver();
489         assert(driver);
490
491         JMutexAutoLock lock(m_atlaspointer_cache_mutex);
492
493         // Create an image of the right size
494         core::dimension2d<u32> atlas_dim(1024,1024);
495         video::IImage *atlas_img =
496                         driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
497         assert(atlas_img);
498
499         /*
500                 A list of stuff to add. This should contain as much of the
501                 stuff shown in game as possible, to minimize texture changes.
502         */
503
504         core::array<std::string> sourcelist;
505
506         sourcelist.push_back("stone.png");
507         sourcelist.push_back("mud.png");
508         sourcelist.push_back("sand.png");
509         sourcelist.push_back("grass.png");
510         sourcelist.push_back("grass_footsteps.png");
511         sourcelist.push_back("tree.png");
512         sourcelist.push_back("tree_top.png");
513         sourcelist.push_back("water.png");
514         sourcelist.push_back("leaves.png");
515         sourcelist.push_back("glass.png");
516         sourcelist.push_back("mud.png^grass_side.png");
517         sourcelist.push_back("cobble.png");
518         
519         sourcelist.push_back("stone.png^mineral_coal.png");
520         sourcelist.push_back("stone.png^mineral_iron.png");
521         sourcelist.push_back("mud.png^mineral_coal.png");
522         sourcelist.push_back("mud.png^mineral_iron.png");
523         sourcelist.push_back("sand.png^mineral_coal.png");
524         sourcelist.push_back("sand.png^mineral_iron.png");
525         
526         // Padding to disallow texture bleeding
527         s32 padding = 16;
528
529         /*
530                 First pass: generate almost everything
531         */
532         core::position2d<s32> pos_in_atlas(0,0);
533         
534         pos_in_atlas.Y += padding;
535
536         for(u32 i=0; i<sourcelist.size(); i++)
537         {
538                 std::string name = sourcelist[i];
539
540                 /*video::IImage *img = driver->createImageFromFile(
541                                 getTexturePath(name.c_str()).c_str());
542                 if(img == NULL)
543                         continue;
544                 
545                 core::dimension2d<u32> dim = img->getDimension();
546                 // Make a copy with the right color format
547                 video::IImage *img2 =
548                                 driver->createImage(video::ECF_A8R8G8B8, dim);
549                 img->copyTo(img2);
550                 img->drop();*/
551                 
552                 // Generate image by name
553                 video::IImage *img2 = generate_image_from_scratch(name, m_device);
554                 if(img2 == NULL)
555                 {
556                         dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
557                         continue;
558                 }
559
560                 core::dimension2d<u32> dim = img2->getDimension();
561
562                 // Don't add to atlas if image is large
563                 core::dimension2d<u32> max_size_in_atlas(32,32);
564                 if(dim.Width > max_size_in_atlas.Width
565                 || dim.Height > max_size_in_atlas.Height)
566                 {
567                         dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
568                                         <<"\""<<name<<"\" because image is large"<<std::endl;
569                         continue;
570                 }
571
572                 // Stop making atlas if atlas is full
573                 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
574                 {
575                         dstream<<"WARNING: TextureSource::buildMainAtlas(): "
576                                         <<"Atlas is full, not adding more textures."
577                                         <<std::endl;
578                         break;
579                 }
580                 
581                 // Tile it a few times in the X direction
582                 u16 xwise_tiling = 16;
583                 for(u32 j=0; j<xwise_tiling; j++)
584                 {
585                         // Copy the copy to the atlas
586                         img2->copyToWithAlpha(atlas_img,
587                                         pos_in_atlas + v2s32(j*dim.Width,0),
588                                         core::rect<s32>(v2s32(0,0), dim),
589                                         video::SColor(255,255,255,255),
590                                         NULL);
591                 }
592
593                 // Copy the borders a few times to disallow texture bleeding
594                 for(u32 side=0; side<2; side++) // top and bottom
595                 for(s32 y0=0; y0<padding; y0++)
596                 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
597                 {
598                         s32 dst_y;
599                         s32 src_y;
600                         if(side==0)
601                         {
602                                 dst_y = y0 + pos_in_atlas.Y + dim.Height;
603                                 src_y = pos_in_atlas.Y + dim.Height - 1;
604                         }
605                         else
606                         {
607                                 dst_y = -y0 + pos_in_atlas.Y-1;
608                                 src_y = pos_in_atlas.Y;
609                         }
610                         s32 x = x0 + pos_in_atlas.X * dim.Width;
611                         video::SColor c = atlas_img->getPixel(x, src_y);
612                         atlas_img->setPixel(x,dst_y,c);
613                 }
614
615                 img2->drop();
616
617                 /*
618                         Add texture to caches
619                 */
620                 
621                 // Get next id
622                 u32 id = m_atlaspointer_cache.size();
623
624                 // Create AtlasPointer
625                 AtlasPointer ap(id);
626                 ap.atlas = NULL; // Set on the second pass
627                 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
628                                 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
629                 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
630                                 (float)dim.Width/(float)atlas_dim.Height);
631                 ap.tiled = xwise_tiling;
632
633                 // Create SourceAtlasPointer and add to containers
634                 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
635                 m_atlaspointer_cache.push_back(nap);
636                 m_name_to_id.insert(name, id);
637                         
638                 // Increment position
639                 pos_in_atlas.Y += dim.Height + padding * 2;
640         }
641
642         /*
643                 Make texture
644         */
645         video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
646         assert(t);
647
648         /*
649                 Second pass: set texture pointer in generated AtlasPointers
650         */
651         for(u32 i=0; i<sourcelist.size(); i++)
652         {
653                 std::string name = sourcelist[i];
654                 if(m_name_to_id.find(name) == NULL)
655                         continue;
656                 u32 id = m_name_to_id[name];
657                 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
658                 m_atlaspointer_cache[id].a.atlas = t;
659         }
660
661         /*
662                 Write image to file so that it can be inspected
663         */
664         /*driver->writeImageToFile(atlas_img, 
665                         getTexturePath("main_atlas.png").c_str());*/
666 }
667
668 video::IImage* generate_image_from_scratch(std::string name,
669                 IrrlichtDevice *device)
670 {
671         dstream<<"INFO: generate_image_from_scratch(): "
672                         "name="<<name<<std::endl;
673         
674         video::IVideoDriver* driver = device->getVideoDriver();
675         assert(driver);
676
677         /*
678                 Get the base image
679         */
680
681         video::IImage *baseimg = NULL;
682
683         char separator = '^';
684
685         // Find last meta separator in name
686         s32 last_separator_position = -1;
687         for(s32 i=name.size()-1; i>=0; i--)
688         {
689                 if(name[i] == separator)
690                 {
691                         last_separator_position = i;
692                         break;
693                 }
694         }
695
696         /*dstream<<"INFO: generate_image_from_scratch(): "
697                         <<"last_separator_position="<<last_separator_position
698                         <<std::endl;*/
699
700         /*
701                 If separator was found, construct the base name and make the
702                 base image using a recursive call
703         */
704         std::string base_image_name;
705         if(last_separator_position != -1)
706         {
707                 // Construct base name
708                 base_image_name = name.substr(0, last_separator_position);
709                 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
710                                 " to get base image, name="<<base_image_name<<std::endl;
711                 baseimg = generate_image_from_scratch(base_image_name, device);
712         }
713         
714         /*
715                 Parse out the last part of the name of the image and act
716                 according to it
717         */
718
719         std::string last_part_of_name = name.substr(last_separator_position+1);
720         dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
721         
722         // Generate image according to part of name
723         if(generate_image(last_part_of_name, baseimg, device) == false)
724         {
725                 dstream<<"INFO: generate_image_from_scratch(): "
726                                 "failed to generate \""<<last_part_of_name<<"\""
727                                 <<std::endl;
728                 return NULL;
729         }
730         
731         return baseimg;
732 }
733
734 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
735                 IrrlichtDevice *device)
736 {
737         video::IVideoDriver* driver = device->getVideoDriver();
738         assert(driver);
739
740         // Stuff starting with [ are special commands
741         if(part_of_name[0] != '[')
742         {
743                 // A normal texture; load it from a file
744                 std::string path = getTexturePath(part_of_name.c_str());
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: Could not load image \""<<part_of_name
753                                         <<"\" from path \""<<path<<"\""
754                                         <<" while building texture"<<std::endl;
755
756                         //return false;
757
758                         dstream<<"WARNING: Creating a dummy"<<" image for \""
759                                         <<part_of_name<<"\""<<std::endl;
760
761                         // Just create a dummy image
762                         //core::dimension2d<u32> dim(2,2);
763                         core::dimension2d<u32> dim(1,1);
764                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
765                         assert(image);
766                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
767                         image->setPixel(1,0, video::SColor(255,0,255,0));
768                         image->setPixel(0,1, video::SColor(255,0,0,255));
769                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
770                         image->setPixel(0,0, video::SColor(255,myrand()%256,
771                                         myrand()%256,myrand()%256));
772                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
773                                         myrand()%256,myrand()%256));
774                         image->setPixel(0,1, video::SColor(255,myrand()%256,
775                                         myrand()%256,myrand()%256));
776                         image->setPixel(1,1, video::SColor(255,myrand()%256,
777                                         myrand()%256,myrand()%256));*/
778                 }
779
780                 // If base image is NULL, load as base.
781                 if(baseimg == NULL)
782                 {
783                         dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
784                         /*
785                                 Copy it this way to get an alpha channel.
786                                 Otherwise images with alpha cannot be blitted on 
787                                 images that don't have alpha in the original file.
788                         */
789                         core::dimension2d<u32> dim = image->getDimension();
790                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
791                         image->copyTo(baseimg);
792                         image->drop();
793                 }
794                 // Else blit on base.
795                 else
796                 {
797                         dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
798                         // Size of the copied area
799                         core::dimension2d<u32> dim = image->getDimension();
800                         //core::dimension2d<u32> dim(16,16);
801                         // Position to copy the blitted to in the base image
802                         core::position2d<s32> pos_to(0,0);
803                         // Position to copy the blitted from in the blitted image
804                         core::position2d<s32> pos_from(0,0);
805                         // Blit
806                         image->copyToWithAlpha(baseimg, pos_to,
807                                         core::rect<s32>(pos_from, dim),
808                                         video::SColor(255,255,255,255),
809                                         NULL);
810                         // Drop image
811                         image->drop();
812                 }
813         }
814         else
815         {
816                 // A special texture modification
817
818                 dstream<<"INFO: getTextureIdDirect(): generating special "
819                                 <<"modification \""<<part_of_name<<"\""
820                                 <<std::endl;
821                 
822                 /*
823                         This is the simplest of all; it just adds stuff to the
824                         name so that a separate texture is created.
825
826                         It is used to make textures for stuff that doesn't want
827                         to implement getting the texture from a bigger texture
828                         atlas.
829                 */
830                 if(part_of_name == "[forcesingle")
831                 {
832                 }
833                 /*
834                         [crackN
835                         Adds a cracking texture
836                 */
837                 else if(part_of_name.substr(0,6) == "[crack")
838                 {
839                         if(baseimg == NULL)
840                         {
841                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
842                                                 <<"for part_of_name="<<part_of_name
843                                                 <<", cancelling."<<std::endl;
844                                 return false;
845                         }
846                         
847                         // Crack image number
848                         u16 progression = stoi(part_of_name.substr(6));
849
850                         // Size of the base image
851                         core::dimension2d<u32> dim_base = baseimg->getDimension();
852                         
853                         /*
854                                 Load crack image.
855
856                                 It is an image with a number of cracking stages
857                                 horizontally tiled.
858                         */
859                         video::IImage *img_crack = driver->createImageFromFile(
860                                         getTexturePath("crack.png").c_str());
861                 
862                         if(img_crack)
863                         {
864                                 // Dimension of original image
865                                 core::dimension2d<u32> dim_crack
866                                                 = img_crack->getDimension();
867                                 // Count of crack stages
868                                 u32 crack_count = dim_crack.Height / dim_crack.Width;
869                                 // Limit progression
870                                 if(progression > crack_count-1)
871                                         progression = crack_count-1;
872                                 // Dimension of a single scaled crack stage
873                                 core::dimension2d<u32> dim_crack_scaled_single(
874                                         dim_base.Width,
875                                         dim_base.Height
876                                 );
877                                 // Dimension of scaled size
878                                 core::dimension2d<u32> dim_crack_scaled(
879                                         dim_crack_scaled_single.Width,
880                                         dim_crack_scaled_single.Height * crack_count
881                                 );
882                                 // Create scaled crack image
883                                 video::IImage *img_crack_scaled = driver->createImage(
884                                                 video::ECF_A8R8G8B8, dim_crack_scaled);
885                                 if(img_crack_scaled)
886                                 {
887                                         // Scale crack image by copying
888                                         img_crack->copyToScaling(img_crack_scaled);
889                                         
890                                         // Position to copy the crack from
891                                         core::position2d<s32> pos_crack_scaled(
892                                                 0,
893                                                 dim_crack_scaled_single.Height * progression
894                                         );
895                                         
896                                         // This tiling does nothing currently but is useful
897                                         for(u32 y0=0; y0<dim_base.Height
898                                                         / dim_crack_scaled_single.Height; y0++)
899                                         for(u32 x0=0; x0<dim_base.Width
900                                                         / dim_crack_scaled_single.Width; x0++)
901                                         {
902                                                 // Position to copy the crack to in the base image
903                                                 core::position2d<s32> pos_base(
904                                                         x0*dim_crack_scaled_single.Width,
905                                                         y0*dim_crack_scaled_single.Height
906                                                 );
907                                                 // Rectangle to copy the crack from on the scaled image
908                                                 core::rect<s32> rect_crack_scaled(
909                                                         pos_crack_scaled,
910                                                         dim_crack_scaled_single
911                                                 );
912                                                 // Copy it
913                                                 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
914                                                                 rect_crack_scaled,
915                                                                 video::SColor(255,255,255,255),
916                                                                 NULL);
917                                         }
918
919                                         img_crack_scaled->drop();
920                                 }
921                                 
922                                 img_crack->drop();
923                         }
924                 }
925                 /*
926                         [combine:WxH:X,Y=filename:X,Y=filename2
927                         Creates a bigger texture from an amount of smaller ones
928                 */
929                 else if(part_of_name.substr(0,8) == "[combine")
930                 {
931                         Strfnd sf(part_of_name);
932                         sf.next(":");
933                         u32 w0 = stoi(sf.next("x"));
934                         u32 h0 = stoi(sf.next(":"));
935                         dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
936                         core::dimension2d<u32> dim(w0,h0);
937                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
938                         while(sf.atend() == false)
939                         {
940                                 u32 x = stoi(sf.next(","));
941                                 u32 y = stoi(sf.next("="));
942                                 std::string filename = sf.next(":");
943                                 dstream<<"INFO: Adding \""<<filename
944                                                 <<"\" to combined ("<<x<<","<<y<<")"
945                                                 <<std::endl;
946                                 video::IImage *img = driver->createImageFromFile(
947                                                 getTexturePath(filename.c_str()).c_str());
948                                 if(img)
949                                 {
950                                         core::dimension2d<u32> dim = img->getDimension();
951                                         dstream<<"INFO: Size "<<dim.Width
952                                                         <<"x"<<dim.Height<<std::endl;
953                                         core::position2d<s32> pos_base(x, y);
954                                         video::IImage *img2 =
955                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
956                                         img->copyTo(img2);
957                                         img->drop();
958                                         img2->copyToWithAlpha(baseimg, pos_base,
959                                                         core::rect<s32>(v2s32(0,0), dim),
960                                                         video::SColor(255,255,255,255),
961                                                         NULL);
962                                         img2->drop();
963                                 }
964                                 else
965                                 {
966                                         dstream<<"WARNING: img==NULL"<<std::endl;
967                                 }
968                         }
969                 }
970                 /*
971                         [progressbarN
972                         Adds a progress bar, 0.0 <= N <= 1.0
973                 */
974                 else if(part_of_name.substr(0,12) == "[progressbar")
975                 {
976                         if(baseimg == NULL)
977                         {
978                                 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
979                                                 <<"for part_of_name="<<part_of_name
980                                                 <<", cancelling."<<std::endl;
981                                 return false;
982                         }
983
984                         float value = stof(part_of_name.substr(12));
985                         make_progressbar(value, baseimg);
986                 }
987                 /*
988                         "[noalpha:filename.png"
989                         Use an image without it's alpha channel.
990                         Used for the leaves texture when in old leaves mode, so
991                         that the transparent parts don't look completely black 
992                         when simple alpha channel is used for rendering.
993                 */
994                 else if(part_of_name.substr(0,8) == "[noalpha")
995                 {
996                         if(baseimg != NULL)
997                         {
998                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
999                                                 <<"for part_of_name="<<part_of_name
1000                                                 <<", cancelling."<<std::endl;
1001                                 return false;
1002                         }
1003
1004                         std::string filename = part_of_name.substr(9);
1005
1006                         std::string path = getTexturePath(filename.c_str());
1007
1008                         dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
1009                                         <<"\""<<std::endl;
1010                         
1011                         video::IImage *image = driver->createImageFromFile(path.c_str());
1012                         
1013                         if(image == NULL)
1014                         {
1015                                 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
1016                                                 <<path<<"\" failed"<<std::endl;
1017                         }
1018                         else
1019                         {
1020                                 core::dimension2d<u32> dim = image->getDimension();
1021                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1022                                 
1023                                 // Set alpha to full
1024                                 for(u32 y=0; y<dim.Height; y++)
1025                                 for(u32 x=0; x<dim.Width; x++)
1026                                 {
1027                                         video::SColor c = image->getPixel(x,y);
1028                                         c.setAlpha(255);
1029                                         image->setPixel(x,y,c);
1030                                 }
1031                                 // Blit
1032                                 image->copyTo(baseimg);
1033
1034                                 image->drop();
1035                         }
1036                 }
1037                 /*
1038                         [inventorycube{topimage{leftimage{rightimage
1039                         In every subimage, replace ^ with &.
1040                         Create an "inventory cube".
1041                         NOTE: This should be used only on its own.
1042                         Example (a grass block (not actually used in game):
1043                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1044                 */
1045                 else if(part_of_name.substr(0,14) == "[inventorycube")
1046                 {
1047                         if(baseimg != NULL)
1048                         {
1049                                 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
1050                                                 <<"for part_of_name="<<part_of_name
1051                                                 <<", cancelling."<<std::endl;
1052                                 return false;
1053                         }
1054
1055                         str_replace_char(part_of_name, '&', '^');
1056                         Strfnd sf(part_of_name);
1057                         sf.next("{");
1058                         std::string imagename_top = sf.next("{");
1059                         std::string imagename_left = sf.next("{");
1060                         std::string imagename_right = sf.next("{");
1061
1062 #if 1
1063                         //TODO
1064
1065                         if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1066                         {
1067                                 dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET"
1068                                                 " not supported. Creating fallback image"<<std::endl;
1069                                 baseimg = generate_image_from_scratch(
1070                                                 imagename_top, device);
1071                                 return true;
1072                         }
1073                         
1074                         u32 w0 = 64;
1075                         u32 h0 = 64;
1076                         dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1077                         core::dimension2d<u32> dim(w0,h0);
1078                         
1079                         // Generate images for the faces of the cube
1080                         video::IImage *img_top = generate_image_from_scratch(
1081                                         imagename_top, device);
1082                         video::IImage *img_left = generate_image_from_scratch(
1083                                         imagename_left, device);
1084                         video::IImage *img_right = generate_image_from_scratch(
1085                                         imagename_right, device);
1086                         assert(img_top && img_left && img_right);
1087
1088                         // TODO: Create textures from images
1089                         video::ITexture *texture_top = driver->addTexture(
1090                                         (imagename_top + "__temp__").c_str(), img_top);
1091                         assert(texture_top);
1092                         
1093                         // Drop images
1094                         img_top->drop();
1095                         img_left->drop();
1096                         img_right->drop();
1097                         
1098                         // Create render target texture
1099                         video::ITexture *rtt = NULL;
1100                         std::string rtt_name = part_of_name + "_RTT";
1101                         rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1102                                         video::ECF_A8R8G8B8);
1103                         assert(rtt);
1104                         
1105                         // Set render target
1106                         driver->setRenderTarget(rtt, true, true,
1107                                         video::SColor(0,0,0,0));
1108                         
1109                         // Get a scene manager
1110                         scene::ISceneManager *smgr_main = device->getSceneManager();
1111                         assert(smgr_main);
1112                         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1113                         assert(smgr);
1114                         
1115                         /*
1116                                 Create scene:
1117                                 - An unit cube is centered at 0,0,0
1118                                 - Camera looks at cube from Y+, Z- towards Y-, Z+
1119                                 NOTE: Cube has to be changed to something else because
1120                                 the textures cannot be set individually (or can they?)
1121                         */
1122
1123                         scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1124                                         v3f(0,0,0), v3f(0, 45, 0));
1125                         // Set texture of cube
1126                         cube->setMaterialTexture(0, texture_top);
1127                         //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1128                         cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1129                         cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1130
1131                         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1132                                         v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1133                         // Set orthogonal projection
1134                         core::CMatrix4<f32> pm;
1135                         pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1136                         camera->setProjectionMatrix(pm, true);
1137
1138                         /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1139                                         v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1140
1141                         smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1142
1143                         // Render scene
1144                         driver->beginScene(true, true, video::SColor(0,0,0,0));
1145                         smgr->drawAll();
1146                         driver->endScene();
1147                         
1148                         // NOTE: The scene nodes should not be dropped, otherwise
1149                         //       smgr->drop() segfaults
1150                         /*cube->drop();
1151                         camera->drop();
1152                         light->drop();*/
1153                         // Drop scene manager
1154                         smgr->drop();
1155                         
1156                         // Unset render target
1157                         driver->setRenderTarget(0, true, true, 0);
1158
1159                         //TODO: Free textures of images
1160                         driver->removeTexture(texture_top);
1161                         
1162                         // Create image of render target
1163                         video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1164
1165                         assert(image);
1166                         
1167                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1168
1169                         if(image)
1170                         {
1171                                 image->copyTo(baseimg);
1172                                 image->drop();
1173                         }
1174 #endif
1175                 }
1176                 else
1177                 {
1178                         dstream<<"WARNING: getTextureIdDirect(): Invalid "
1179                                         " modification: \""<<part_of_name<<"\""<<std::endl;
1180                 }
1181         }
1182
1183         return true;
1184 }
1185
1186 void make_progressbar(float value, video::IImage *image)
1187 {
1188         if(image == NULL)
1189                 return;
1190         
1191         core::dimension2d<u32> size = image->getDimension();
1192
1193         u32 barheight = size.Height/16;
1194         u32 barpad_x = size.Width/16;
1195         u32 barpad_y = size.Height/16;
1196         u32 barwidth = size.Width - barpad_x*2;
1197         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1198
1199         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1200
1201         video::SColor active(255,255,0,0);
1202         video::SColor inactive(255,0,0,0);
1203         for(u32 x0=0; x0<barwidth; x0++)
1204         {
1205                 video::SColor *c;
1206                 if(x0 < barvalue_i)
1207                         c = &active;
1208                 else
1209                         c = &inactive;
1210                 u32 x = x0 + barpos.X;
1211                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1212                 {
1213                         image->setPixel(x,y, *c);
1214                 }
1215         }
1216 }
1217