3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
22 #include "main.h" // for g_settings
26 #include <ICameraSceneNode.h>
30 A cache from texture name to texture path
32 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
35 Replaces the filename extension.
37 std::string image = "a/image.png"
38 replace_ext(image, "jpg")
39 -> image = "a/image.jpg"
40 Returns true on success.
42 static bool replace_ext(std::string &path, const char *ext)
46 // Find place of last dot, fail if \ or / found.
48 for(s32 i=path.size()-1; i>=0; i--)
56 if(path[i] == '\\' || path[i] == '/')
59 // If not found, return an empty string
62 // Else make the new path
63 path = path.substr(0, last_dot_i+1) + ext;
68 Find out the full path of an image by trying different filename
73 static std::string getImagePath(std::string path)
75 // A NULL-ended list of possible image extensions
76 const char *extensions[] = {
77 "png", "jpg", "bmp", "tga",
78 "pcx", "ppm", "psd", "wal", "rgb",
82 const char **ext = extensions;
84 bool r = replace_ext(path, *ext);
87 if(fs::PathExists(path))
90 while((++ext) != NULL);
96 Gets the path to a texture by first checking if the texture exists
97 in texture_path and if not, using the data path.
99 Checks all supported extensions by replacing the original extension.
101 If not found, returns "".
103 Utilizes a thread-safe cache.
105 std::string getTexturePath(const std::string &filename)
107 std::string fullpath = "";
111 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
116 Check from texture_path
118 std::string texture_path = g_settings->get("texture_path");
119 if(texture_path != "")
121 std::string testpath = texture_path + '/' + filename;
122 // Check all filename extensions. Returns "" if not found.
123 fullpath = getImagePath(testpath);
127 Check from default data directory
131 std::string testpath = porting::getDataPath(filename.c_str());
132 // Check all filename extensions. Returns "" if not found.
133 fullpath = getImagePath(testpath);
136 // Add to cache (also an empty result is cached)
137 g_texturename_to_path_cache.set(filename, fullpath);
147 TextureSource::TextureSource(IrrlichtDevice *device):
149 m_main_atlas_image(NULL),
150 m_main_atlas_texture(NULL)
154 m_atlaspointer_cache_mutex.Init();
156 m_main_thread = get_current_thread_id();
158 // Add a NULL AtlasPointer as the first index, named ""
159 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
160 m_name_to_id[""] = 0;
162 // Build main texture atlas
163 if(g_settings->getBool("enable_texture_atlas"))
166 infostream<<"Not building texture atlas."<<std::endl;
169 TextureSource::~TextureSource()
173 void TextureSource::processQueue()
178 if(m_get_texture_queue.size() > 0)
180 GetRequest<std::string, u32, u8, u8>
181 request = m_get_texture_queue.pop();
183 infostream<<"TextureSource::processQueue(): "
184 <<"got texture request with "
185 <<"name=\""<<request.key<<"\""
188 GetResult<std::string, u32, u8, u8>
190 result.key = request.key;
191 result.callers = request.callers;
192 result.item = getTextureIdDirect(request.key);
194 request.dest->push_back(result);
198 u32 TextureSource::getTextureId(const std::string &name)
200 //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
204 See if texture already exists
206 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
207 core::map<std::string, u32>::Node *n;
208 n = m_name_to_id.find(name);
211 return n->getValue();
218 if(get_current_thread_id() == m_main_thread)
220 return getTextureIdDirect(name);
224 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
226 // We're gonna ask the result to be put into here
227 ResultQueue<std::string, u32, u8, u8> result_queue;
229 // Throw a request in
230 m_get_texture_queue.add(name, 0, 0, &result_queue);
232 infostream<<"Waiting for texture from main thread, name=\""
233 <<name<<"\""<<std::endl;
237 // Wait result for a second
238 GetResult<std::string, u32, u8, u8>
239 result = result_queue.pop_front(1000);
241 // Check that at least something worked OK
242 assert(result.key == name);
246 catch(ItemNotFoundException &e)
248 infostream<<"Waiting for texture timed out."<<std::endl;
253 infostream<<"getTextureId(): Failed"<<std::endl;
258 // Draw a progress bar on the image
259 void make_progressbar(float value, video::IImage *image);
262 Generate image based on a string like "stone.png" or "[crack0".
263 if baseimg is NULL, it is created. Otherwise stuff is made on it.
265 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
266 IrrlichtDevice *device);
269 Generates an image from a full string like
270 "stone.png^mineral_coal.png^[crack0".
272 This is used by buildMainAtlas().
274 video::IImage* generate_image_from_scratch(std::string name,
275 IrrlichtDevice *device);
278 This method generates all the textures
280 u32 TextureSource::getTextureIdDirect(const std::string &name)
282 //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
284 // Empty name means texture 0
287 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
292 Calling only allowed from main thread
294 if(get_current_thread_id() != m_main_thread)
296 errorstream<<"TextureSource::getTextureIdDirect() "
297 "called not from main thread"<<std::endl;
302 See if texture already exists
305 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
307 core::map<std::string, u32>::Node *n;
308 n = m_name_to_id.find(name);
311 infostream<<"getTextureIdDirect(): \""<<name
312 <<"\" found in cache"<<std::endl;
313 return n->getValue();
317 infostream<<"getTextureIdDirect(): \""<<name
318 <<"\" NOT found in cache. Creating it."<<std::endl;
324 char separator = '^';
327 This is set to the id of the base image.
328 If left 0, there is no base image and a completely new image
331 u32 base_image_id = 0;
333 // Find last meta separator in name
334 s32 last_separator_position = -1;
335 for(s32 i=name.size()-1; i>=0; i--)
337 if(name[i] == separator)
339 last_separator_position = i;
344 If separator was found, construct the base name and make the
345 base image using a recursive call
347 std::string base_image_name;
348 if(last_separator_position != -1)
350 // Construct base name
351 base_image_name = name.substr(0, last_separator_position);
352 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
353 " to get base image of \""<<name<<"\" = \""
354 <<base_image_name<<"\""<<std::endl;*/
355 base_image_id = getTextureIdDirect(base_image_name);
358 //infostream<<"base_image_id="<<base_image_id<<std::endl;
360 video::IVideoDriver* driver = m_device->getVideoDriver();
363 video::ITexture *t = NULL;
366 An image will be built from files and then converted into a texture.
368 video::IImage *baseimg = NULL;
370 // If a base image was found, copy it to baseimg
371 if(base_image_id != 0)
373 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
375 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
377 video::IImage *image = ap.atlas_img;
381 infostream<<"getTextureIdDirect(): NULL image in "
382 <<"cache: \""<<base_image_name<<"\""
387 core::dimension2d<u32> dim = ap.intsize;
389 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
391 core::position2d<s32> pos_to(0,0);
392 core::position2d<s32> pos_from = ap.intpos;
396 v2s32(0,0), // position in target
397 core::rect<s32>(pos_from, dim) // from
400 /*infostream<<"getTextureIdDirect(): Loaded \""
401 <<base_image_name<<"\" from image cache"
407 Parse out the last part of the name of the image and act
411 std::string last_part_of_name = name.substr(last_separator_position+1);
412 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
414 // Generate image according to part of name
415 if(generate_image(last_part_of_name, baseimg, m_device) == false)
417 infostream<<"getTextureIdDirect(): "
418 "failed to generate \""<<last_part_of_name<<"\""
422 // If no resulting image, print a warning
425 infostream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
426 " create texture \""<<name<<"\""<<std::endl;
431 // Create texture from resulting image
432 t = driver->addTexture(name.c_str(), baseimg);
436 Add texture to caches (add NULL textures too)
439 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
441 u32 id = m_atlaspointer_cache.size();
447 core::dimension2d<u32> baseimg_dim(0,0);
449 baseimg_dim = baseimg->getDimension();
450 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
451 m_atlaspointer_cache.push_back(nap);
452 m_name_to_id.insert(name, id);
454 /*infostream<<"getTextureIdDirect(): "
455 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
460 std::string TextureSource::getTextureName(u32 id)
462 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
464 if(id >= m_atlaspointer_cache.size())
466 infostream<<"TextureSource::getTextureName(): id="<<id
467 <<" >= m_atlaspointer_cache.size()="
468 <<m_atlaspointer_cache.size()<<std::endl;
472 return m_atlaspointer_cache[id].name;
476 AtlasPointer TextureSource::getTexture(u32 id)
478 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
480 if(id >= m_atlaspointer_cache.size())
481 return AtlasPointer(0, NULL);
483 return m_atlaspointer_cache[id].a;
486 void TextureSource::buildMainAtlas()
488 infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
490 //return; // Disable (for testing)
492 video::IVideoDriver* driver = m_device->getVideoDriver();
495 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
497 // Create an image of the right size
498 core::dimension2d<u32> atlas_dim(1024,1024);
499 video::IImage *atlas_img =
500 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
502 if(atlas_img == NULL)
504 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
505 "image; not building texture atlas."<<std::endl;
510 A list of stuff to include in the texture atlas.
512 It is a single-dimensional texture atlas due to the need to tile
515 It should contain as much of the stuff shown in game as possible,
516 to minimize texture changes.
518 It fills up quickly, so do not add anything that isn't contained
519 in most MapBlocks. E.g. mese isn't suitable but stone is.
522 core::array<std::string> sourcelist;
524 sourcelist.push_back("stone.png");
525 sourcelist.push_back("mud.png");
526 sourcelist.push_back("sand.png");
527 sourcelist.push_back("grass.png");
528 sourcelist.push_back("grass_footsteps.png");
529 sourcelist.push_back("tree.png");
530 sourcelist.push_back("tree_top.png");
531 sourcelist.push_back("water.png");
532 sourcelist.push_back("leaves.png");
533 sourcelist.push_back("glass.png");
534 sourcelist.push_back("mud.png^grass_side.png");
535 sourcelist.push_back("cobble.png");
536 sourcelist.push_back("mossycobble.png");
537 sourcelist.push_back("gravel.png");
538 sourcelist.push_back("jungletree.png");
540 sourcelist.push_back("stone.png^mineral_coal.png");
541 sourcelist.push_back("stone.png^mineral_iron.png");
543 // Padding to disallow texture bleeding
547 First pass: generate almost everything
549 core::position2d<s32> pos_in_atlas(0,0);
551 pos_in_atlas.Y += padding;
553 for(u32 i=0; i<sourcelist.size(); i++)
555 std::string name = sourcelist[i];
557 /*video::IImage *img = driver->createImageFromFile(
558 getTexturePath(name.c_str()).c_str());
562 core::dimension2d<u32> dim = img->getDimension();
563 // Make a copy with the right color format
564 video::IImage *img2 =
565 driver->createImage(video::ECF_A8R8G8B8, dim);
569 // Generate image by name
570 video::IImage *img2 = generate_image_from_scratch(name, m_device);
573 infostream<<"TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
577 core::dimension2d<u32> dim = img2->getDimension();
579 // Don't add to atlas if image is large
580 core::dimension2d<u32> max_size_in_atlas(32,32);
581 if(dim.Width > max_size_in_atlas.Width
582 || dim.Height > max_size_in_atlas.Height)
584 infostream<<"TextureSource::buildMainAtlas(): Not adding "
585 <<"\""<<name<<"\" because image is large"<<std::endl;
589 // Stop making atlas if atlas is full
590 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
592 infostream<<"TextureSource::buildMainAtlas(): "
593 <<"Atlas is full, not adding more textures."
598 infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
599 <<"\" to texture atlas"<<std::endl;
601 // Tile it a few times in the X direction
602 u16 xwise_tiling = 16;
603 for(u32 j=0; j<xwise_tiling; j++)
605 // Copy the copy to the atlas
606 img2->copyToWithAlpha(atlas_img,
607 pos_in_atlas + v2s32(j*dim.Width,0),
608 core::rect<s32>(v2s32(0,0), dim),
609 video::SColor(255,255,255,255),
613 // Copy the borders a few times to disallow texture bleeding
614 for(u32 side=0; side<2; side++) // top and bottom
615 for(s32 y0=0; y0<padding; y0++)
616 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
622 dst_y = y0 + pos_in_atlas.Y + dim.Height;
623 src_y = pos_in_atlas.Y + dim.Height - 1;
627 dst_y = -y0 + pos_in_atlas.Y-1;
628 src_y = pos_in_atlas.Y;
630 s32 x = x0 + pos_in_atlas.X * dim.Width;
631 video::SColor c = atlas_img->getPixel(x, src_y);
632 atlas_img->setPixel(x,dst_y,c);
638 Add texture to caches
642 u32 id = m_atlaspointer_cache.size();
644 // Create AtlasPointer
646 ap.atlas = NULL; // Set on the second pass
647 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
648 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
649 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
650 (float)dim.Width/(float)atlas_dim.Height);
651 ap.tiled = xwise_tiling;
653 // Create SourceAtlasPointer and add to containers
654 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
655 m_atlaspointer_cache.push_back(nap);
656 m_name_to_id.insert(name, id);
658 // Increment position
659 pos_in_atlas.Y += dim.Height + padding * 2;
665 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
669 Second pass: set texture pointer in generated AtlasPointers
671 for(u32 i=0; i<sourcelist.size(); i++)
673 std::string name = sourcelist[i];
674 if(m_name_to_id.find(name) == NULL)
676 u32 id = m_name_to_id[name];
677 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
678 m_atlaspointer_cache[id].a.atlas = t;
682 Write image to file so that it can be inspected
684 /*driver->writeImageToFile(atlas_img,
685 getTexturePath("main_atlas.png").c_str());*/
688 video::IImage* generate_image_from_scratch(std::string name,
689 IrrlichtDevice *device)
691 /*infostream<<"generate_image_from_scratch(): "
692 "\""<<name<<"\""<<std::endl;*/
694 video::IVideoDriver* driver = device->getVideoDriver();
701 video::IImage *baseimg = NULL;
703 char separator = '^';
705 // Find last meta separator in name
706 s32 last_separator_position = -1;
707 for(s32 i=name.size()-1; i>=0; i--)
709 if(name[i] == separator)
711 last_separator_position = i;
716 /*infostream<<"generate_image_from_scratch(): "
717 <<"last_separator_position="<<last_separator_position
721 If separator was found, construct the base name and make the
722 base image using a recursive call
724 std::string base_image_name;
725 if(last_separator_position != -1)
727 // Construct base name
728 base_image_name = name.substr(0, last_separator_position);
729 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
730 " to get base image of \""<<name<<"\" = \""
731 <<base_image_name<<"\""<<std::endl;*/
732 baseimg = generate_image_from_scratch(base_image_name, device);
736 Parse out the last part of the name of the image and act
740 std::string last_part_of_name = name.substr(last_separator_position+1);
741 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
743 // Generate image according to part of name
744 if(generate_image(last_part_of_name, baseimg, device) == false)
746 infostream<<"generate_image_from_scratch(): "
747 "failed to generate \""<<last_part_of_name<<"\""
755 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
756 IrrlichtDevice *device)
758 video::IVideoDriver* driver = device->getVideoDriver();
761 // Stuff starting with [ are special commands
762 if(part_of_name[0] != '[')
764 // A normal texture; load it from a file
765 std::string path = getTexturePath(part_of_name.c_str());
766 /*infostream<<"generate_image(): Loading path \""<<path
769 video::IImage *image = driver->createImageFromFile(path.c_str());
773 infostream<<"generate_image(): Could not load image \""
774 <<part_of_name<<"\" from path \""<<path<<"\""
775 <<" while building texture"<<std::endl;
779 infostream<<"generate_image(): Creating a dummy"
780 <<" image for \""<<part_of_name<<"\""<<std::endl;
782 // Just create a dummy image
783 //core::dimension2d<u32> dim(2,2);
784 core::dimension2d<u32> dim(1,1);
785 image = driver->createImage(video::ECF_A8R8G8B8, dim);
787 /*image->setPixel(0,0, video::SColor(255,255,0,0));
788 image->setPixel(1,0, video::SColor(255,0,255,0));
789 image->setPixel(0,1, video::SColor(255,0,0,255));
790 image->setPixel(1,1, video::SColor(255,255,0,255));*/
791 image->setPixel(0,0, video::SColor(255,myrand()%256,
792 myrand()%256,myrand()%256));
793 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
794 myrand()%256,myrand()%256));
795 image->setPixel(0,1, video::SColor(255,myrand()%256,
796 myrand()%256,myrand()%256));
797 image->setPixel(1,1, video::SColor(255,myrand()%256,
798 myrand()%256,myrand()%256));*/
801 // If base image is NULL, load as base.
804 //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
806 Copy it this way to get an alpha channel.
807 Otherwise images with alpha cannot be blitted on
808 images that don't have alpha in the original file.
810 core::dimension2d<u32> dim = image->getDimension();
811 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
812 image->copyTo(baseimg);
815 // Else blit on base.
818 //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
819 // Size of the copied area
820 core::dimension2d<u32> dim = image->getDimension();
821 //core::dimension2d<u32> dim(16,16);
822 // Position to copy the blitted to in the base image
823 core::position2d<s32> pos_to(0,0);
824 // Position to copy the blitted from in the blitted image
825 core::position2d<s32> pos_from(0,0);
827 image->copyToWithAlpha(baseimg, pos_to,
828 core::rect<s32>(pos_from, dim),
829 video::SColor(255,255,255,255),
837 // A special texture modification
839 infostream<<"generate_image(): generating special "
840 <<"modification \""<<part_of_name<<"\""
844 This is the simplest of all; it just adds stuff to the
845 name so that a separate texture is created.
847 It is used to make textures for stuff that doesn't want
848 to implement getting the texture from a bigger texture
851 if(part_of_name == "[forcesingle")
856 Adds a cracking texture
858 else if(part_of_name.substr(0,6) == "[crack")
862 infostream<<"generate_image(): baseimg==NULL "
863 <<"for part_of_name=\""<<part_of_name
864 <<"\", cancelling."<<std::endl;
868 // Crack image number
869 u16 progression = stoi(part_of_name.substr(6));
871 // Size of the base image
872 core::dimension2d<u32> dim_base = baseimg->getDimension();
877 It is an image with a number of cracking stages
880 video::IImage *img_crack = driver->createImageFromFile(
881 getTexturePath("crack.png").c_str());
885 // Dimension of original image
886 core::dimension2d<u32> dim_crack
887 = img_crack->getDimension();
888 // Count of crack stages
889 u32 crack_count = dim_crack.Height / dim_crack.Width;
891 if(progression > crack_count-1)
892 progression = crack_count-1;
893 // Dimension of a single scaled crack stage
894 core::dimension2d<u32> dim_crack_scaled_single(
898 // Dimension of scaled size
899 core::dimension2d<u32> dim_crack_scaled(
900 dim_crack_scaled_single.Width,
901 dim_crack_scaled_single.Height * crack_count
903 // Create scaled crack image
904 video::IImage *img_crack_scaled = driver->createImage(
905 video::ECF_A8R8G8B8, dim_crack_scaled);
908 // Scale crack image by copying
909 img_crack->copyToScaling(img_crack_scaled);
911 // Position to copy the crack from
912 core::position2d<s32> pos_crack_scaled(
914 dim_crack_scaled_single.Height * progression
917 // This tiling does nothing currently but is useful
918 for(u32 y0=0; y0<dim_base.Height
919 / dim_crack_scaled_single.Height; y0++)
920 for(u32 x0=0; x0<dim_base.Width
921 / dim_crack_scaled_single.Width; x0++)
923 // Position to copy the crack to in the base image
924 core::position2d<s32> pos_base(
925 x0*dim_crack_scaled_single.Width,
926 y0*dim_crack_scaled_single.Height
928 // Rectangle to copy the crack from on the scaled image
929 core::rect<s32> rect_crack_scaled(
931 dim_crack_scaled_single
934 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
936 video::SColor(255,255,255,255),
940 img_crack_scaled->drop();
947 [combine:WxH:X,Y=filename:X,Y=filename2
948 Creates a bigger texture from an amount of smaller ones
950 else if(part_of_name.substr(0,8) == "[combine")
952 Strfnd sf(part_of_name);
954 u32 w0 = stoi(sf.next("x"));
955 u32 h0 = stoi(sf.next(":"));
956 infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
957 core::dimension2d<u32> dim(w0,h0);
958 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
959 while(sf.atend() == false)
961 u32 x = stoi(sf.next(","));
962 u32 y = stoi(sf.next("="));
963 std::string filename = sf.next(":");
964 infostream<<"Adding \""<<filename
965 <<"\" to combined ("<<x<<","<<y<<")"
967 video::IImage *img = driver->createImageFromFile(
968 getTexturePath(filename.c_str()).c_str());
971 core::dimension2d<u32> dim = img->getDimension();
972 infostream<<"Size "<<dim.Width
973 <<"x"<<dim.Height<<std::endl;
974 core::position2d<s32> pos_base(x, y);
975 video::IImage *img2 =
976 driver->createImage(video::ECF_A8R8G8B8, dim);
979 img2->copyToWithAlpha(baseimg, pos_base,
980 core::rect<s32>(v2s32(0,0), dim),
981 video::SColor(255,255,255,255),
987 infostream<<"img==NULL"<<std::endl;
993 Adds a progress bar, 0.0 <= N <= 1.0
995 else if(part_of_name.substr(0,12) == "[progressbar")
999 infostream<<"generate_image(): baseimg==NULL "
1000 <<"for part_of_name=\""<<part_of_name
1001 <<"\", cancelling."<<std::endl;
1005 float value = stof(part_of_name.substr(12));
1006 make_progressbar(value, baseimg);
1009 "[noalpha:filename.png"
1010 Use an image without it's alpha channel.
1011 Used for the leaves texture when in old leaves mode, so
1012 that the transparent parts don't look completely black
1013 when simple alpha channel is used for rendering.
1015 else if(part_of_name.substr(0,8) == "[noalpha")
1019 infostream<<"generate_image(): baseimg!=NULL "
1020 <<"for part_of_name=\""<<part_of_name
1021 <<"\", cancelling."<<std::endl;
1025 std::string filename = part_of_name.substr(9);
1027 std::string path = getTexturePath(filename.c_str());
1029 infostream<<"generate_image(): Loading path \""<<path
1032 video::IImage *image = driver->createImageFromFile(path.c_str());
1036 infostream<<"generate_image(): Loading path \""
1037 <<path<<"\" failed"<<std::endl;
1041 core::dimension2d<u32> dim = image->getDimension();
1042 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1044 // Set alpha to full
1045 for(u32 y=0; y<dim.Height; y++)
1046 for(u32 x=0; x<dim.Width; x++)
1048 video::SColor c = image->getPixel(x,y);
1050 image->setPixel(x,y,c);
1053 image->copyTo(baseimg);
1059 "[makealpha:R,G,B:filename.png"
1060 Use an image with converting one color to transparent.
1062 else if(part_of_name.substr(0,11) == "[makealpha:")
1066 infostream<<"generate_image(): baseimg!=NULL "
1067 <<"for part_of_name=\""<<part_of_name
1068 <<"\", cancelling."<<std::endl;
1072 Strfnd sf(part_of_name.substr(11));
1073 u32 r1 = stoi(sf.next(","));
1074 u32 g1 = stoi(sf.next(","));
1075 u32 b1 = stoi(sf.next(":"));
1076 std::string filename = sf.next("");
1078 std::string path = getTexturePath(filename.c_str());
1080 infostream<<"generate_image(): Loading path \""<<path
1083 video::IImage *image = driver->createImageFromFile(path.c_str());
1087 infostream<<"generate_image(): Loading path \""
1088 <<path<<"\" failed"<<std::endl;
1092 core::dimension2d<u32> dim = image->getDimension();
1093 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1095 for(u32 y=0; y<dim.Height; y++)
1096 for(u32 x=0; x<dim.Width; x++)
1098 video::SColor c = image->getPixel(x,y);
1100 u32 g = c.getGreen();
1101 u32 b = c.getBlue();
1102 if(!(r == r1 && g == g1 && b == b1))
1105 image->setPixel(x,y,c);
1108 image->copyTo(baseimg);
1114 "[makealpha2:R,G,B;R2,G2,B2:filename.png"
1115 Use an image with converting two colors to transparent.
1117 else if(part_of_name.substr(0,12) == "[makealpha2:")
1121 infostream<<"generate_image(): baseimg!=NULL "
1122 <<"for part_of_name=\""<<part_of_name
1123 <<"\", cancelling."<<std::endl;
1127 Strfnd sf(part_of_name.substr(12));
1128 u32 r1 = stoi(sf.next(","));
1129 u32 g1 = stoi(sf.next(","));
1130 u32 b1 = stoi(sf.next(";"));
1131 u32 r2 = stoi(sf.next(","));
1132 u32 g2 = stoi(sf.next(","));
1133 u32 b2 = stoi(sf.next(":"));
1134 std::string filename = sf.next("");
1136 std::string path = getTexturePath(filename.c_str());
1138 infostream<<"generate_image(): Loading path \""<<path
1141 video::IImage *image = driver->createImageFromFile(path.c_str());
1145 infostream<<"generate_image(): Loading path \""
1146 <<path<<"\" failed"<<std::endl;
1150 core::dimension2d<u32> dim = image->getDimension();
1151 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1153 for(u32 y=0; y<dim.Height; y++)
1154 for(u32 x=0; x<dim.Width; x++)
1156 video::SColor c = image->getPixel(x,y);
1158 u32 g = c.getGreen();
1159 u32 b = c.getBlue();
1160 if(!(r == r1 && g == g1 && b == b1) &&
1161 !(r == r2 && g == g2 && b == b2))
1164 image->setPixel(x,y,c);
1167 image->copyTo(baseimg);
1173 [inventorycube{topimage{leftimage{rightimage
1174 In every subimage, replace ^ with &.
1175 Create an "inventory cube".
1176 NOTE: This should be used only on its own.
1177 Example (a grass block (not actually used in game):
1178 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1180 else if(part_of_name.substr(0,14) == "[inventorycube")
1184 infostream<<"generate_image(): baseimg!=NULL "
1185 <<"for part_of_name=\""<<part_of_name
1186 <<"\", cancelling."<<std::endl;
1190 str_replace_char(part_of_name, '&', '^');
1191 Strfnd sf(part_of_name);
1193 std::string imagename_top = sf.next("{");
1194 std::string imagename_left = sf.next("{");
1195 std::string imagename_right = sf.next("{");
1200 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1202 infostream<<"generate_image(): EVDF_RENDER_TO_TARGET"
1203 " not supported. Creating fallback image"<<std::endl;
1204 baseimg = generate_image_from_scratch(
1205 imagename_top, device);
1211 //infostream<<"inventorycube w="<<w0<<" h="<<h0<<std::endl;
1212 core::dimension2d<u32> dim(w0,h0);
1214 // Generate images for the faces of the cube
1215 video::IImage *img_top = generate_image_from_scratch(
1216 imagename_top, device);
1217 video::IImage *img_left = generate_image_from_scratch(
1218 imagename_left, device);
1219 video::IImage *img_right = generate_image_from_scratch(
1220 imagename_right, device);
1221 assert(img_top && img_left && img_right);
1223 // TODO: Create textures from images
1224 video::ITexture *texture_top = driver->addTexture(
1225 (imagename_top + "__temp__").c_str(), img_top);
1226 assert(texture_top);
1233 // Create render target texture
1234 video::ITexture *rtt = NULL;
1235 std::string rtt_name = part_of_name + "_RTT";
1236 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1237 video::ECF_A8R8G8B8);
1240 // Set render target
1241 driver->setRenderTarget(rtt, true, true,
1242 video::SColor(0,0,0,0));
1244 // Get a scene manager
1245 scene::ISceneManager *smgr_main = device->getSceneManager();
1247 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1252 - An unit cube is centered at 0,0,0
1253 - Camera looks at cube from Y+, Z- towards Y-, Z+
1254 NOTE: Cube has to be changed to something else because
1255 the textures cannot be set individually (or can they?)
1258 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1259 v3f(0,0,0), v3f(0, 45, 0));
1260 // Set texture of cube
1261 cube->setMaterialTexture(0, texture_top);
1262 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1263 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1264 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1266 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1267 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1268 // Set orthogonal projection
1269 core::CMatrix4<f32> pm;
1270 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1271 camera->setProjectionMatrix(pm, true);
1273 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1274 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1276 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1279 driver->beginScene(true, true, video::SColor(0,0,0,0));
1283 // NOTE: The scene nodes should not be dropped, otherwise
1284 // smgr->drop() segfaults
1288 // Drop scene manager
1291 // Unset render target
1292 driver->setRenderTarget(0, true, true, 0);
1294 //TODO: Free textures of images
1295 driver->removeTexture(texture_top);
1297 // Create image of render target
1298 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1302 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1306 image->copyTo(baseimg);
1313 infostream<<"generate_image(): Invalid "
1314 " modification: \""<<part_of_name<<"\""<<std::endl;
1321 void make_progressbar(float value, video::IImage *image)
1326 core::dimension2d<u32> size = image->getDimension();
1328 u32 barheight = size.Height/16;
1329 u32 barpad_x = size.Width/16;
1330 u32 barpad_y = size.Height/16;
1331 u32 barwidth = size.Width - barpad_x*2;
1332 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1334 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1336 video::SColor active(255,255,0,0);
1337 video::SColor inactive(255,0,0,0);
1338 for(u32 x0=0; x0<barwidth; x0++)
1345 u32 x = x0 + barpos.X;
1346 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1348 image->setPixel(x,y, *c);