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>
28 #include "mapnode.h" // For texture atlas making
29 #include "mineral.h" // For texture atlas making
32 A cache from texture name to texture path
34 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
37 Replaces the filename extension.
39 std::string image = "a/image.png"
40 replace_ext(image, "jpg")
41 -> image = "a/image.jpg"
42 Returns true on success.
44 static bool replace_ext(std::string &path, const char *ext)
48 // Find place of last dot, fail if \ or / found.
50 for(s32 i=path.size()-1; i>=0; i--)
58 if(path[i] == '\\' || path[i] == '/')
61 // If not found, return an empty string
64 // Else make the new path
65 path = path.substr(0, last_dot_i+1) + ext;
70 Find out the full path of an image by trying different filename
75 static std::string getImagePath(std::string path)
77 // A NULL-ended list of possible image extensions
78 const char *extensions[] = {
79 "png", "jpg", "bmp", "tga",
80 "pcx", "ppm", "psd", "wal", "rgb",
84 const char **ext = extensions;
86 bool r = replace_ext(path, *ext);
89 if(fs::PathExists(path))
92 while((++ext) != NULL);
98 Gets the path to a texture by first checking if the texture exists
99 in texture_path and if not, using the data path.
101 Checks all supported extensions by replacing the original extension.
103 If not found, returns "".
105 Utilizes a thread-safe cache.
107 std::string getTexturePath(const std::string &filename)
109 std::string fullpath = "";
113 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
118 Check from texture_path
120 std::string texture_path = g_settings->get("texture_path");
121 if(texture_path != "")
123 std::string testpath = texture_path + DIR_DELIM + filename;
124 // Check all filename extensions. Returns "" if not found.
125 fullpath = getImagePath(testpath);
129 Check from default data directory
133 std::string testpath = porting::getDataPath(filename.c_str());
134 // Check all filename extensions. Returns "" if not found.
135 fullpath = getImagePath(testpath);
138 // Add to cache (also an empty result is cached)
139 g_texturename_to_path_cache.set(filename, fullpath);
149 TextureSource::TextureSource(IrrlichtDevice *device):
151 m_main_atlas_image(NULL),
152 m_main_atlas_texture(NULL)
156 m_atlaspointer_cache_mutex.Init();
158 m_main_thread = get_current_thread_id();
160 // Add a NULL AtlasPointer as the first index, named ""
161 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
162 m_name_to_id[""] = 0;
164 // Build main texture atlas
165 if(g_settings->getBool("enable_texture_atlas"))
168 infostream<<"Not building texture atlas."<<std::endl;
171 TextureSource::~TextureSource()
175 void TextureSource::processQueue()
180 if(m_get_texture_queue.size() > 0)
182 GetRequest<std::string, u32, u8, u8>
183 request = m_get_texture_queue.pop();
185 infostream<<"TextureSource::processQueue(): "
186 <<"got texture request with "
187 <<"name=\""<<request.key<<"\""
190 GetResult<std::string, u32, u8, u8>
192 result.key = request.key;
193 result.callers = request.callers;
194 result.item = getTextureIdDirect(request.key);
196 request.dest->push_back(result);
200 u32 TextureSource::getTextureId(const std::string &name)
202 //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
206 See if texture already exists
208 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
209 core::map<std::string, u32>::Node *n;
210 n = m_name_to_id.find(name);
213 return n->getValue();
220 if(get_current_thread_id() == m_main_thread)
222 return getTextureIdDirect(name);
226 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
228 // We're gonna ask the result to be put into here
229 ResultQueue<std::string, u32, u8, u8> result_queue;
231 // Throw a request in
232 m_get_texture_queue.add(name, 0, 0, &result_queue);
234 infostream<<"Waiting for texture from main thread, name=\""
235 <<name<<"\""<<std::endl;
239 // Wait result for a second
240 GetResult<std::string, u32, u8, u8>
241 result = result_queue.pop_front(1000);
243 // Check that at least something worked OK
244 assert(result.key == name);
248 catch(ItemNotFoundException &e)
250 infostream<<"Waiting for texture timed out."<<std::endl;
255 infostream<<"getTextureId(): Failed"<<std::endl;
260 // Draw a progress bar on the image
261 void make_progressbar(float value, video::IImage *image);
264 Generate image based on a string like "stone.png" or "[crack0".
265 if baseimg is NULL, it is created. Otherwise stuff is made on it.
267 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
268 IrrlichtDevice *device);
271 Generates an image from a full string like
272 "stone.png^mineral_coal.png^[crack0".
274 This is used by buildMainAtlas().
276 video::IImage* generate_image_from_scratch(std::string name,
277 IrrlichtDevice *device);
280 This method generates all the textures
282 u32 TextureSource::getTextureIdDirect(const std::string &name)
284 //infostream<<"getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
286 // Empty name means texture 0
289 infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
294 Calling only allowed from main thread
296 if(get_current_thread_id() != m_main_thread)
298 errorstream<<"TextureSource::getTextureIdDirect() "
299 "called not from main thread"<<std::endl;
304 See if texture already exists
307 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
309 core::map<std::string, u32>::Node *n;
310 n = m_name_to_id.find(name);
313 infostream<<"getTextureIdDirect(): \""<<name
314 <<"\" found in cache"<<std::endl;
315 return n->getValue();
319 infostream<<"getTextureIdDirect(): \""<<name
320 <<"\" NOT found in cache. Creating it."<<std::endl;
326 char separator = '^';
329 This is set to the id of the base image.
330 If left 0, there is no base image and a completely new image
333 u32 base_image_id = 0;
335 // Find last meta separator in name
336 s32 last_separator_position = -1;
337 for(s32 i=name.size()-1; i>=0; i--)
339 if(name[i] == separator)
341 last_separator_position = i;
346 If separator was found, construct the base name and make the
347 base image using a recursive call
349 std::string base_image_name;
350 if(last_separator_position != -1)
352 // Construct base name
353 base_image_name = name.substr(0, last_separator_position);
354 /*infostream<<"getTextureIdDirect(): Calling itself recursively"
355 " to get base image of \""<<name<<"\" = \""
356 <<base_image_name<<"\""<<std::endl;*/
357 base_image_id = getTextureIdDirect(base_image_name);
360 //infostream<<"base_image_id="<<base_image_id<<std::endl;
362 video::IVideoDriver* driver = m_device->getVideoDriver();
365 video::ITexture *t = NULL;
368 An image will be built from files and then converted into a texture.
370 video::IImage *baseimg = NULL;
372 // If a base image was found, copy it to baseimg
373 if(base_image_id != 0)
375 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
377 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
379 video::IImage *image = ap.atlas_img;
383 infostream<<"getTextureIdDirect(): NULL image in "
384 <<"cache: \""<<base_image_name<<"\""
389 core::dimension2d<u32> dim = ap.intsize;
391 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
393 core::position2d<s32> pos_to(0,0);
394 core::position2d<s32> pos_from = ap.intpos;
398 v2s32(0,0), // position in target
399 core::rect<s32>(pos_from, dim) // from
402 /*infostream<<"getTextureIdDirect(): Loaded \""
403 <<base_image_name<<"\" from image cache"
409 Parse out the last part of the name of the image and act
413 std::string last_part_of_name = name.substr(last_separator_position+1);
414 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
416 // Generate image according to part of name
417 if(generate_image(last_part_of_name, baseimg, m_device) == false)
419 infostream<<"getTextureIdDirect(): "
420 "failed to generate \""<<last_part_of_name<<"\""
424 // If no resulting image, print a warning
427 infostream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
428 " create texture \""<<name<<"\""<<std::endl;
433 // Create texture from resulting image
434 t = driver->addTexture(name.c_str(), baseimg);
438 Add texture to caches (add NULL textures too)
441 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
443 u32 id = m_atlaspointer_cache.size();
449 core::dimension2d<u32> baseimg_dim(0,0);
451 baseimg_dim = baseimg->getDimension();
452 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
453 m_atlaspointer_cache.push_back(nap);
454 m_name_to_id.insert(name, id);
456 /*infostream<<"getTextureIdDirect(): "
457 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
462 std::string TextureSource::getTextureName(u32 id)
464 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
466 if(id >= m_atlaspointer_cache.size())
468 infostream<<"TextureSource::getTextureName(): id="<<id
469 <<" >= m_atlaspointer_cache.size()="
470 <<m_atlaspointer_cache.size()<<std::endl;
474 return m_atlaspointer_cache[id].name;
478 AtlasPointer TextureSource::getTexture(u32 id)
480 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
482 if(id >= m_atlaspointer_cache.size())
483 return AtlasPointer(0, NULL);
485 return m_atlaspointer_cache[id].a;
488 void TextureSource::buildMainAtlas()
490 infostream<<"TextureSource::buildMainAtlas()"<<std::endl;
492 //return; // Disable (for testing)
494 video::IVideoDriver* driver = m_device->getVideoDriver();
497 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
499 // Create an image of the right size
500 core::dimension2d<u32> atlas_dim(1024,1024);
501 video::IImage *atlas_img =
502 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
504 if(atlas_img == NULL)
506 errorstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
507 "image; not building texture atlas."<<std::endl;
512 Grab list of stuff to include in the texture atlas from the
513 main content features
516 core::map<std::string, bool> sourcelist;
518 for(u16 j=0; j<MAX_CONTENT+1; j++)
520 if(j == CONTENT_IGNORE || j == CONTENT_AIR)
522 ContentFeatures *f = &content_features(j);
523 for(core::map<std::string, bool>::Iterator
524 i = f->used_texturenames.getIterator();
525 i.atEnd() == false; i++)
527 std::string name = i.getNode()->getKey();
528 sourcelist[name] = true;
530 if(f->often_contains_mineral){
531 for(int k=1; k<MINERAL_COUNT; k++){
532 std::string mineraltexture = mineral_block_texture(k);
533 std::string fulltexture = name + "^" + mineraltexture;
534 sourcelist[fulltexture] = true;
540 infostream<<"Creating texture atlas out of textures: ";
541 for(core::map<std::string, bool>::Iterator
542 i = sourcelist.getIterator();
543 i.atEnd() == false; i++)
545 std::string name = i.getNode()->getKey();
546 infostream<<"\""<<name<<"\" ";
548 infostream<<std::endl;
550 // Padding to disallow texture bleeding
553 s32 column_width = 256;
554 s32 column_padding = 16;
557 First pass: generate almost everything
559 core::position2d<s32> pos_in_atlas(0,0);
561 pos_in_atlas.Y = padding;
563 for(core::map<std::string, bool>::Iterator
564 i = sourcelist.getIterator();
565 i.atEnd() == false; i++)
567 std::string name = i.getNode()->getKey();
569 /*video::IImage *img = driver->createImageFromFile(
570 getTexturePath(name.c_str()).c_str());
574 core::dimension2d<u32> dim = img->getDimension();
575 // Make a copy with the right color format
576 video::IImage *img2 =
577 driver->createImage(video::ECF_A8R8G8B8, dim);
581 // Generate image by name
582 video::IImage *img2 = generate_image_from_scratch(name, m_device);
585 infostream<<"TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
589 core::dimension2d<u32> dim = img2->getDimension();
591 // Don't add to atlas if image is large
592 core::dimension2d<u32> max_size_in_atlas(32,32);
593 if(dim.Width > max_size_in_atlas.Width
594 || dim.Height > max_size_in_atlas.Height)
596 infostream<<"TextureSource::buildMainAtlas(): Not adding "
597 <<"\""<<name<<"\" because image is large"<<std::endl;
601 // Wrap columns and stop making atlas if atlas is full
602 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
604 if(pos_in_atlas.X > (s32)atlas_dim.Width - 256 - padding){
605 errorstream<<"TextureSource::buildMainAtlas(): "
606 <<"Atlas is full, not adding more textures."
610 pos_in_atlas.Y = padding;
611 pos_in_atlas.X += column_width + column_padding;
614 infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
615 <<"\" to texture atlas"<<std::endl;
617 // Tile it a few times in the X direction
618 u16 xwise_tiling = column_width / dim.Width;
619 if(xwise_tiling > 16) // Limit to 16 (more gives no benefit)
621 for(u32 j=0; j<xwise_tiling; j++)
623 // Copy the copy to the atlas
624 img2->copyToWithAlpha(atlas_img,
625 pos_in_atlas + v2s32(j*dim.Width,0),
626 core::rect<s32>(v2s32(0,0), dim),
627 video::SColor(255,255,255,255),
631 // Copy the borders a few times to disallow texture bleeding
632 for(u32 side=0; side<2; side++) // top and bottom
633 for(s32 y0=0; y0<padding; y0++)
634 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
640 dst_y = y0 + pos_in_atlas.Y + dim.Height;
641 src_y = pos_in_atlas.Y + dim.Height - 1;
645 dst_y = -y0 + pos_in_atlas.Y-1;
646 src_y = pos_in_atlas.Y;
648 s32 x = x0 + pos_in_atlas.X;
649 video::SColor c = atlas_img->getPixel(x, src_y);
650 atlas_img->setPixel(x,dst_y,c);
656 Add texture to caches
660 u32 id = m_atlaspointer_cache.size();
662 // Create AtlasPointer
664 ap.atlas = NULL; // Set on the second pass
665 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
666 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
667 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
668 (float)dim.Width/(float)atlas_dim.Height);
669 ap.tiled = xwise_tiling;
671 // Create SourceAtlasPointer and add to containers
672 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
673 m_atlaspointer_cache.push_back(nap);
674 m_name_to_id.insert(name, id);
676 // Increment position
677 pos_in_atlas.Y += dim.Height + padding * 2;
683 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
687 Second pass: set texture pointer in generated AtlasPointers
689 for(core::map<std::string, bool>::Iterator
690 i = sourcelist.getIterator();
691 i.atEnd() == false; i++)
693 std::string name = i.getNode()->getKey();
694 if(m_name_to_id.find(name) == NULL)
696 u32 id = m_name_to_id[name];
697 //infostream<<"id of name "<<name<<" is "<<id<<std::endl;
698 m_atlaspointer_cache[id].a.atlas = t;
702 Write image to file so that it can be inspected
704 /*std::string atlaspath = porting::path_userdata
705 + DIR_DELIM + "generated_texture_atlas.png";
706 infostream<<"Removing and writing texture atlas for inspection to "
707 <<atlaspath<<std::endl;
708 fs::RecursiveDelete(atlaspath);
709 driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
712 video::IImage* generate_image_from_scratch(std::string name,
713 IrrlichtDevice *device)
715 /*infostream<<"generate_image_from_scratch(): "
716 "\""<<name<<"\""<<std::endl;*/
718 video::IVideoDriver* driver = device->getVideoDriver();
725 video::IImage *baseimg = NULL;
727 char separator = '^';
729 // Find last meta separator in name
730 s32 last_separator_position = -1;
731 for(s32 i=name.size()-1; i>=0; i--)
733 if(name[i] == separator)
735 last_separator_position = i;
740 /*infostream<<"generate_image_from_scratch(): "
741 <<"last_separator_position="<<last_separator_position
745 If separator was found, construct the base name and make the
746 base image using a recursive call
748 std::string base_image_name;
749 if(last_separator_position != -1)
751 // Construct base name
752 base_image_name = name.substr(0, last_separator_position);
753 /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
754 " to get base image of \""<<name<<"\" = \""
755 <<base_image_name<<"\""<<std::endl;*/
756 baseimg = generate_image_from_scratch(base_image_name, device);
760 Parse out the last part of the name of the image and act
764 std::string last_part_of_name = name.substr(last_separator_position+1);
765 //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
767 // Generate image according to part of name
768 if(generate_image(last_part_of_name, baseimg, device) == false)
770 infostream<<"generate_image_from_scratch(): "
771 "failed to generate \""<<last_part_of_name<<"\""
779 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
780 IrrlichtDevice *device)
782 video::IVideoDriver* driver = device->getVideoDriver();
785 // Stuff starting with [ are special commands
786 if(part_of_name[0] != '[')
788 // A normal texture; load it from a file
789 std::string path = getTexturePath(part_of_name.c_str());
790 /*infostream<<"generate_image(): Loading path \""<<path
793 video::IImage *image = driver->createImageFromFile(path.c_str());
797 infostream<<"generate_image(): Could not load image \""
798 <<part_of_name<<"\" from path \""<<path<<"\""
799 <<" while building texture"<<std::endl;
803 infostream<<"generate_image(): Creating a dummy"
804 <<" image for \""<<part_of_name<<"\""<<std::endl;
806 // Just create a dummy image
807 //core::dimension2d<u32> dim(2,2);
808 core::dimension2d<u32> dim(1,1);
809 image = driver->createImage(video::ECF_A8R8G8B8, dim);
811 /*image->setPixel(0,0, video::SColor(255,255,0,0));
812 image->setPixel(1,0, video::SColor(255,0,255,0));
813 image->setPixel(0,1, video::SColor(255,0,0,255));
814 image->setPixel(1,1, video::SColor(255,255,0,255));*/
815 image->setPixel(0,0, video::SColor(255,myrand()%256,
816 myrand()%256,myrand()%256));
817 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
818 myrand()%256,myrand()%256));
819 image->setPixel(0,1, video::SColor(255,myrand()%256,
820 myrand()%256,myrand()%256));
821 image->setPixel(1,1, video::SColor(255,myrand()%256,
822 myrand()%256,myrand()%256));*/
825 // If base image is NULL, load as base.
828 //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
830 Copy it this way to get an alpha channel.
831 Otherwise images with alpha cannot be blitted on
832 images that don't have alpha in the original file.
834 core::dimension2d<u32> dim = image->getDimension();
835 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
836 image->copyTo(baseimg);
839 // Else blit on base.
842 //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
843 // Size of the copied area
844 core::dimension2d<u32> dim = image->getDimension();
845 //core::dimension2d<u32> dim(16,16);
846 // Position to copy the blitted to in the base image
847 core::position2d<s32> pos_to(0,0);
848 // Position to copy the blitted from in the blitted image
849 core::position2d<s32> pos_from(0,0);
851 image->copyToWithAlpha(baseimg, pos_to,
852 core::rect<s32>(pos_from, dim),
853 video::SColor(255,255,255,255),
861 // A special texture modification
863 infostream<<"generate_image(): generating special "
864 <<"modification \""<<part_of_name<<"\""
868 This is the simplest of all; it just adds stuff to the
869 name so that a separate texture is created.
871 It is used to make textures for stuff that doesn't want
872 to implement getting the texture from a bigger texture
875 if(part_of_name == "[forcesingle")
880 Adds a cracking texture
882 else if(part_of_name.substr(0,6) == "[crack")
886 infostream<<"generate_image(): baseimg==NULL "
887 <<"for part_of_name=\""<<part_of_name
888 <<"\", cancelling."<<std::endl;
892 // Crack image number
893 u16 progression = stoi(part_of_name.substr(6));
895 // Size of the base image
896 core::dimension2d<u32> dim_base = baseimg->getDimension();
901 It is an image with a number of cracking stages
904 video::IImage *img_crack = driver->createImageFromFile(
905 getTexturePath("crack.png").c_str());
909 // Dimension of original image
910 core::dimension2d<u32> dim_crack
911 = img_crack->getDimension();
912 // Count of crack stages
913 u32 crack_count = dim_crack.Height / dim_crack.Width;
915 if(progression > crack_count-1)
916 progression = crack_count-1;
917 // Dimension of a single scaled crack stage
918 core::dimension2d<u32> dim_crack_scaled_single(
922 // Dimension of scaled size
923 core::dimension2d<u32> dim_crack_scaled(
924 dim_crack_scaled_single.Width,
925 dim_crack_scaled_single.Height * crack_count
927 // Create scaled crack image
928 video::IImage *img_crack_scaled = driver->createImage(
929 video::ECF_A8R8G8B8, dim_crack_scaled);
932 // Scale crack image by copying
933 img_crack->copyToScaling(img_crack_scaled);
935 // Position to copy the crack from
936 core::position2d<s32> pos_crack_scaled(
938 dim_crack_scaled_single.Height * progression
941 // This tiling does nothing currently but is useful
942 for(u32 y0=0; y0<dim_base.Height
943 / dim_crack_scaled_single.Height; y0++)
944 for(u32 x0=0; x0<dim_base.Width
945 / dim_crack_scaled_single.Width; x0++)
947 // Position to copy the crack to in the base image
948 core::position2d<s32> pos_base(
949 x0*dim_crack_scaled_single.Width,
950 y0*dim_crack_scaled_single.Height
952 // Rectangle to copy the crack from on the scaled image
953 core::rect<s32> rect_crack_scaled(
955 dim_crack_scaled_single
958 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
960 video::SColor(255,255,255,255),
964 img_crack_scaled->drop();
971 [combine:WxH:X,Y=filename:X,Y=filename2
972 Creates a bigger texture from an amount of smaller ones
974 else if(part_of_name.substr(0,8) == "[combine")
976 Strfnd sf(part_of_name);
978 u32 w0 = stoi(sf.next("x"));
979 u32 h0 = stoi(sf.next(":"));
980 infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
981 core::dimension2d<u32> dim(w0,h0);
982 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
983 while(sf.atend() == false)
985 u32 x = stoi(sf.next(","));
986 u32 y = stoi(sf.next("="));
987 std::string filename = sf.next(":");
988 infostream<<"Adding \""<<filename
989 <<"\" to combined ("<<x<<","<<y<<")"
991 video::IImage *img = driver->createImageFromFile(
992 getTexturePath(filename.c_str()).c_str());
995 core::dimension2d<u32> dim = img->getDimension();
996 infostream<<"Size "<<dim.Width
997 <<"x"<<dim.Height<<std::endl;
998 core::position2d<s32> pos_base(x, y);
999 video::IImage *img2 =
1000 driver->createImage(video::ECF_A8R8G8B8, dim);
1003 img2->copyToWithAlpha(baseimg, pos_base,
1004 core::rect<s32>(v2s32(0,0), dim),
1005 video::SColor(255,255,255,255),
1011 infostream<<"img==NULL"<<std::endl;
1017 Adds a progress bar, 0.0 <= N <= 1.0
1019 else if(part_of_name.substr(0,12) == "[progressbar")
1023 infostream<<"generate_image(): baseimg==NULL "
1024 <<"for part_of_name=\""<<part_of_name
1025 <<"\", cancelling."<<std::endl;
1029 float value = stof(part_of_name.substr(12));
1030 make_progressbar(value, baseimg);
1033 "[noalpha:filename.png"
1034 Use an image without it's alpha channel.
1035 Used for the leaves texture when in old leaves mode, so
1036 that the transparent parts don't look completely black
1037 when simple alpha channel is used for rendering.
1039 else if(part_of_name.substr(0,8) == "[noalpha")
1043 infostream<<"generate_image(): baseimg!=NULL "
1044 <<"for part_of_name=\""<<part_of_name
1045 <<"\", cancelling."<<std::endl;
1049 std::string filename = part_of_name.substr(9);
1051 std::string path = getTexturePath(filename.c_str());
1053 infostream<<"generate_image(): Loading path \""<<path
1056 video::IImage *image = driver->createImageFromFile(path.c_str());
1060 infostream<<"generate_image(): Loading path \""
1061 <<path<<"\" failed"<<std::endl;
1065 core::dimension2d<u32> dim = image->getDimension();
1066 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1068 // Set alpha to full
1069 for(u32 y=0; y<dim.Height; y++)
1070 for(u32 x=0; x<dim.Width; x++)
1072 video::SColor c = image->getPixel(x,y);
1074 image->setPixel(x,y,c);
1077 image->copyTo(baseimg);
1083 "[makealpha:R,G,B:filename.png"
1084 Use an image with converting one color to transparent.
1086 else if(part_of_name.substr(0,11) == "[makealpha:")
1090 infostream<<"generate_image(): baseimg!=NULL "
1091 <<"for part_of_name=\""<<part_of_name
1092 <<"\", cancelling."<<std::endl;
1096 Strfnd sf(part_of_name.substr(11));
1097 u32 r1 = stoi(sf.next(","));
1098 u32 g1 = stoi(sf.next(","));
1099 u32 b1 = stoi(sf.next(":"));
1100 std::string filename = sf.next("");
1102 std::string path = getTexturePath(filename.c_str());
1104 infostream<<"generate_image(): Loading path \""<<path
1107 video::IImage *image = driver->createImageFromFile(path.c_str());
1111 infostream<<"generate_image(): Loading path \""
1112 <<path<<"\" failed"<<std::endl;
1116 core::dimension2d<u32> dim = image->getDimension();
1117 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1120 image->copyTo(baseimg);
1124 for(u32 y=0; y<dim.Height; y++)
1125 for(u32 x=0; x<dim.Width; x++)
1127 video::SColor c = baseimg->getPixel(x,y);
1129 u32 g = c.getGreen();
1130 u32 b = c.getBlue();
1131 if(!(r == r1 && g == g1 && b == b1))
1134 baseimg->setPixel(x,y,c);
1139 "[makealpha2:R,G,B;R2,G2,B2:filename.png"
1140 Use an image with converting two colors to transparent.
1142 else if(part_of_name.substr(0,12) == "[makealpha2:")
1146 infostream<<"generate_image(): baseimg!=NULL "
1147 <<"for part_of_name=\""<<part_of_name
1148 <<"\", cancelling."<<std::endl;
1152 Strfnd sf(part_of_name.substr(12));
1153 u32 r1 = stoi(sf.next(","));
1154 u32 g1 = stoi(sf.next(","));
1155 u32 b1 = stoi(sf.next(";"));
1156 u32 r2 = stoi(sf.next(","));
1157 u32 g2 = stoi(sf.next(","));
1158 u32 b2 = stoi(sf.next(":"));
1159 std::string filename = sf.next("");
1161 std::string path = getTexturePath(filename.c_str());
1163 infostream<<"generate_image(): Loading path \""<<path
1166 video::IImage *image = driver->createImageFromFile(path.c_str());
1170 infostream<<"generate_image(): Loading path \""
1171 <<path<<"\" failed"<<std::endl;
1175 core::dimension2d<u32> dim = image->getDimension();
1176 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1179 image->copyTo(baseimg);
1183 for(u32 y=0; y<dim.Height; y++)
1184 for(u32 x=0; x<dim.Width; x++)
1186 video::SColor c = baseimg->getPixel(x,y);
1188 u32 g = c.getGreen();
1189 u32 b = c.getBlue();
1190 if(!(r == r1 && g == g1 && b == b1) &&
1191 !(r == r2 && g == g2 && b == b2))
1194 baseimg->setPixel(x,y,c);
1199 [inventorycube{topimage{leftimage{rightimage
1200 In every subimage, replace ^ with &.
1201 Create an "inventory cube".
1202 NOTE: This should be used only on its own.
1203 Example (a grass block (not actually used in game):
1204 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1206 else if(part_of_name.substr(0,14) == "[inventorycube")
1210 infostream<<"generate_image(): baseimg!=NULL "
1211 <<"for part_of_name=\""<<part_of_name
1212 <<"\", cancelling."<<std::endl;
1216 str_replace_char(part_of_name, '&', '^');
1217 Strfnd sf(part_of_name);
1219 std::string imagename_top = sf.next("{");
1220 std::string imagename_left = sf.next("{");
1221 std::string imagename_right = sf.next("{");
1226 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1228 infostream<<"generate_image(): EVDF_RENDER_TO_TARGET"
1229 " not supported. Creating fallback image"<<std::endl;
1230 baseimg = generate_image_from_scratch(
1231 imagename_top, device);
1237 //infostream<<"inventorycube w="<<w0<<" h="<<h0<<std::endl;
1238 core::dimension2d<u32> dim(w0,h0);
1240 // Generate images for the faces of the cube
1241 video::IImage *img_top = generate_image_from_scratch(
1242 imagename_top, device);
1243 video::IImage *img_left = generate_image_from_scratch(
1244 imagename_left, device);
1245 video::IImage *img_right = generate_image_from_scratch(
1246 imagename_right, device);
1247 assert(img_top && img_left && img_right);
1249 // TODO: Create textures from images
1250 video::ITexture *texture_top = driver->addTexture(
1251 (imagename_top + "__temp__").c_str(), img_top);
1252 assert(texture_top);
1259 // Create render target texture
1260 video::ITexture *rtt = NULL;
1261 std::string rtt_name = part_of_name + "_RTT";
1262 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1263 video::ECF_A8R8G8B8);
1266 // Set render target
1267 driver->setRenderTarget(rtt, true, true,
1268 video::SColor(0,0,0,0));
1270 // Get a scene manager
1271 scene::ISceneManager *smgr_main = device->getSceneManager();
1273 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1278 - An unit cube is centered at 0,0,0
1279 - Camera looks at cube from Y+, Z- towards Y-, Z+
1280 NOTE: Cube has to be changed to something else because
1281 the textures cannot be set individually (or can they?)
1284 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1285 v3f(0,0,0), v3f(0, 45, 0));
1286 // Set texture of cube
1287 cube->setMaterialTexture(0, texture_top);
1288 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1289 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1290 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1292 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1293 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1294 // Set orthogonal projection
1295 core::CMatrix4<f32> pm;
1296 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1297 camera->setProjectionMatrix(pm, true);
1299 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1300 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1302 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1305 driver->beginScene(true, true, video::SColor(0,0,0,0));
1309 // NOTE: The scene nodes should not be dropped, otherwise
1310 // smgr->drop() segfaults
1314 // Drop scene manager
1317 // Unset render target
1318 driver->setRenderTarget(0, true, true, 0);
1320 //TODO: Free textures of images
1321 driver->removeTexture(texture_top);
1323 // Create image of render target
1324 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1328 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1332 image->copyTo(baseimg);
1339 infostream<<"generate_image(): Invalid "
1340 " modification: \""<<part_of_name<<"\""<<std::endl;
1347 void make_progressbar(float value, video::IImage *image)
1352 core::dimension2d<u32> size = image->getDimension();
1354 u32 barheight = size.Height/16;
1355 u32 barpad_x = size.Width/16;
1356 u32 barpad_y = size.Height/16;
1357 u32 barwidth = size.Width - barpad_x*2;
1358 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1360 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1362 video::SColor active(255,255,0,0);
1363 video::SColor inactive(255,0,0,0);
1364 for(u32 x0=0; x0<barwidth; x0++)
1371 u32 x = x0 + barpos.X;
1372 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1374 image->setPixel(x,y, *c);