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>
29 A cache from texture name to texture path
31 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
34 Replaces the filename extension.
36 std::string image = "a/image.png"
37 replace_ext(image, "jpg")
38 -> image = "a/image.jpg"
39 Returns true on success.
41 static bool replace_ext(std::string &path, const char *ext)
45 // Find place of last dot, fail if \ or / found.
47 for(s32 i=path.size()-1; i>=0; i--)
55 if(path[i] == '\\' || path[i] == '/')
58 // If not found, return an empty string
61 // Else make the new path
62 path = path.substr(0, last_dot_i+1) + ext;
67 Find out the full path of an image by trying different filename
72 static std::string getImagePath(std::string path)
74 // A NULL-ended list of possible image extensions
75 const char *extensions[] = {
76 "png", "jpg", "bmp", "tga",
77 "pcx", "ppm", "psd", "wal", "rgb",
81 const char **ext = extensions;
83 bool r = replace_ext(path, *ext);
86 if(fs::PathExists(path))
89 while((++ext) != NULL);
95 Gets the path to a texture by first checking if the texture exists
96 in texture_path and if not, using the data path.
98 Checks all supported extensions by replacing the original extension.
100 If not found, returns "".
102 Utilizes a thread-safe cache.
104 std::string getTexturePath(const std::string &filename)
106 std::string fullpath = "";
110 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
115 Check from texture_path
117 std::string texture_path = g_settings->get("texture_path");
118 if(texture_path != "")
120 std::string testpath = texture_path + '/' + filename;
121 // Check all filename extensions. Returns "" if not found.
122 fullpath = getImagePath(testpath);
126 Check from default data directory
130 std::string testpath = porting::getDataPath(filename.c_str());
131 // Check all filename extensions. Returns "" if not found.
132 fullpath = getImagePath(testpath);
135 // Add to cache (also an empty result is cached)
136 g_texturename_to_path_cache.set(filename, fullpath);
146 TextureSource::TextureSource(IrrlichtDevice *device):
148 m_main_atlas_image(NULL),
149 m_main_atlas_texture(NULL)
153 m_atlaspointer_cache_mutex.Init();
155 m_main_thread = get_current_thread_id();
157 // Add a NULL AtlasPointer as the first index, named ""
158 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
159 m_name_to_id[""] = 0;
161 // Build main texture atlas
162 if(g_settings->getBool("enable_texture_atlas"))
165 dstream<<"INFO: Not building texture atlas."<<std::endl;
168 TextureSource::~TextureSource()
172 void TextureSource::processQueue()
177 if(m_get_texture_queue.size() > 0)
179 GetRequest<std::string, u32, u8, u8>
180 request = m_get_texture_queue.pop();
182 dstream<<"INFO: TextureSource::processQueue(): "
183 <<"got texture request with "
184 <<"name=\""<<request.key<<"\""
187 GetResult<std::string, u32, u8, u8>
189 result.key = request.key;
190 result.callers = request.callers;
191 result.item = getTextureIdDirect(request.key);
193 request.dest->push_back(result);
197 u32 TextureSource::getTextureId(const std::string &name)
199 //dstream<<"INFO: getTextureId(): \""<<name<<"\""<<std::endl;
203 See if texture already exists
205 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
206 core::map<std::string, u32>::Node *n;
207 n = m_name_to_id.find(name);
210 return n->getValue();
217 if(get_current_thread_id() == m_main_thread)
219 return getTextureIdDirect(name);
223 dstream<<"INFO: getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
225 // We're gonna ask the result to be put into here
226 ResultQueue<std::string, u32, u8, u8> result_queue;
228 // Throw a request in
229 m_get_texture_queue.add(name, 0, 0, &result_queue);
231 dstream<<"INFO: Waiting for texture from main thread, name=\""
232 <<name<<"\""<<std::endl;
236 // Wait result for a second
237 GetResult<std::string, u32, u8, u8>
238 result = result_queue.pop_front(1000);
240 // Check that at least something worked OK
241 assert(result.key == name);
245 catch(ItemNotFoundException &e)
247 dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
252 dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
257 // Draw a progress bar on the image
258 void make_progressbar(float value, video::IImage *image);
261 Generate image based on a string like "stone.png" or "[crack0".
262 if baseimg is NULL, it is created. Otherwise stuff is made on it.
264 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
265 IrrlichtDevice *device);
268 Generates an image from a full string like
269 "stone.png^mineral_coal.png^[crack0".
271 This is used by buildMainAtlas().
273 video::IImage* generate_image_from_scratch(std::string name,
274 IrrlichtDevice *device);
277 This method generates all the textures
279 u32 TextureSource::getTextureIdDirect(const std::string &name)
281 //dstream<<"INFO: getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
283 // Empty name means texture 0
286 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
291 Calling only allowed from main thread
293 if(get_current_thread_id() != m_main_thread)
295 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
296 "called not from main thread"<<std::endl;
301 See if texture already exists
304 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
306 core::map<std::string, u32>::Node *n;
307 n = m_name_to_id.find(name);
310 dstream<<"INFO: getTextureIdDirect(): \""<<name
311 <<"\" found in cache"<<std::endl;
312 return n->getValue();
316 dstream<<"INFO: getTextureIdDirect(): \""<<name
317 <<"\" NOT found in cache. Creating it."<<std::endl;
323 char separator = '^';
326 This is set to the id of the base image.
327 If left 0, there is no base image and a completely new image
330 u32 base_image_id = 0;
332 // Find last meta separator in name
333 s32 last_separator_position = -1;
334 for(s32 i=name.size()-1; i>=0; i--)
336 if(name[i] == separator)
338 last_separator_position = i;
343 If separator was found, construct the base name and make the
344 base image using a recursive call
346 std::string base_image_name;
347 if(last_separator_position != -1)
349 // Construct base name
350 base_image_name = name.substr(0, last_separator_position);
351 /*dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
352 " to get base image of \""<<name<<"\" = \""
353 <<base_image_name<<"\""<<std::endl;*/
354 base_image_id = getTextureIdDirect(base_image_name);
357 //dstream<<"base_image_id="<<base_image_id<<std::endl;
359 video::IVideoDriver* driver = m_device->getVideoDriver();
362 video::ITexture *t = NULL;
365 An image will be built from files and then converted into a texture.
367 video::IImage *baseimg = NULL;
369 // If a base image was found, copy it to baseimg
370 if(base_image_id != 0)
372 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
374 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
376 video::IImage *image = ap.atlas_img;
380 dstream<<"WARNING: getTextureIdDirect(): NULL image in "
381 <<"cache: \""<<base_image_name<<"\""
386 core::dimension2d<u32> dim = ap.intsize;
388 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
390 core::position2d<s32> pos_to(0,0);
391 core::position2d<s32> pos_from = ap.intpos;
395 v2s32(0,0), // position in target
396 core::rect<s32>(pos_from, dim) // from
399 /*dstream<<"INFO: getTextureIdDirect(): Loaded \""
400 <<base_image_name<<"\" from image cache"
406 Parse out the last part of the name of the image and act
410 std::string last_part_of_name = name.substr(last_separator_position+1);
411 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
413 // Generate image according to part of name
414 if(generate_image(last_part_of_name, baseimg, m_device) == false)
416 dstream<<"INFO: getTextureIdDirect(): "
417 "failed to generate \""<<last_part_of_name<<"\""
421 // If no resulting image, print a warning
424 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
425 " create texture \""<<name<<"\""<<std::endl;
430 // Create texture from resulting image
431 t = driver->addTexture(name.c_str(), baseimg);
435 Add texture to caches (add NULL textures too)
438 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
440 u32 id = m_atlaspointer_cache.size();
446 core::dimension2d<u32> baseimg_dim(0,0);
448 baseimg_dim = baseimg->getDimension();
449 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
450 m_atlaspointer_cache.push_back(nap);
451 m_name_to_id.insert(name, id);
453 /*dstream<<"INFO: getTextureIdDirect(): "
454 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
459 std::string TextureSource::getTextureName(u32 id)
461 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
463 if(id >= m_atlaspointer_cache.size())
465 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
466 <<" >= m_atlaspointer_cache.size()="
467 <<m_atlaspointer_cache.size()<<std::endl;
471 return m_atlaspointer_cache[id].name;
475 AtlasPointer TextureSource::getTexture(u32 id)
477 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
479 if(id >= m_atlaspointer_cache.size())
480 return AtlasPointer(0, NULL);
482 return m_atlaspointer_cache[id].a;
485 void TextureSource::buildMainAtlas()
487 dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
489 //return; // Disable (for testing)
491 video::IVideoDriver* driver = m_device->getVideoDriver();
494 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
496 // Create an image of the right size
497 core::dimension2d<u32> atlas_dim(1024,1024);
498 video::IImage *atlas_img =
499 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
501 if(atlas_img == NULL)
503 dstream<<"TextureSource::buildMainAtlas(): Failed to create atlas "
504 "image; not building texture atlas."<<std::endl;
509 A list of stuff to include in the texture atlas.
511 It is a single-dimensional texture atlas due to the need to tile
514 It should contain as much of the stuff shown in game as possible,
515 to minimize texture changes.
517 It fills up quickly, so do not add anything that isn't contained
518 in most MapBlocks. E.g. mese isn't suitable but stone is.
521 core::array<std::string> sourcelist;
523 sourcelist.push_back("stone.png");
524 sourcelist.push_back("mud.png");
525 sourcelist.push_back("sand.png");
526 sourcelist.push_back("grass.png");
527 sourcelist.push_back("grass_footsteps.png");
528 sourcelist.push_back("tree.png");
529 sourcelist.push_back("tree_top.png");
530 sourcelist.push_back("water.png");
531 sourcelist.push_back("leaves.png");
532 sourcelist.push_back("glass.png");
533 sourcelist.push_back("mud.png^grass_side.png");
534 sourcelist.push_back("cobble.png");
535 sourcelist.push_back("mossycobble.png");
536 sourcelist.push_back("gravel.png");
537 sourcelist.push_back("jungletree.png");
539 sourcelist.push_back("stone.png^mineral_coal.png");
540 sourcelist.push_back("stone.png^mineral_iron.png");
542 // Padding to disallow texture bleeding
546 First pass: generate almost everything
548 core::position2d<s32> pos_in_atlas(0,0);
550 pos_in_atlas.Y += padding;
552 for(u32 i=0; i<sourcelist.size(); i++)
554 std::string name = sourcelist[i];
556 /*video::IImage *img = driver->createImageFromFile(
557 getTexturePath(name.c_str()).c_str());
561 core::dimension2d<u32> dim = img->getDimension();
562 // Make a copy with the right color format
563 video::IImage *img2 =
564 driver->createImage(video::ECF_A8R8G8B8, dim);
568 // Generate image by name
569 video::IImage *img2 = generate_image_from_scratch(name, m_device);
572 dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
576 core::dimension2d<u32> dim = img2->getDimension();
578 // Don't add to atlas if image is large
579 core::dimension2d<u32> max_size_in_atlas(32,32);
580 if(dim.Width > max_size_in_atlas.Width
581 || dim.Height > max_size_in_atlas.Height)
583 dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
584 <<"\""<<name<<"\" because image is large"<<std::endl;
588 // Stop making atlas if atlas is full
589 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
591 dstream<<"WARNING: TextureSource::buildMainAtlas(): "
592 <<"Atlas is full, not adding more textures."
597 dstream<<"INFO: TextureSource::buildMainAtlas(): Adding \""<<name
598 <<"\" to texture atlas"<<std::endl;
600 // Tile it a few times in the X direction
601 u16 xwise_tiling = 16;
602 for(u32 j=0; j<xwise_tiling; j++)
604 // Copy the copy to the atlas
605 img2->copyToWithAlpha(atlas_img,
606 pos_in_atlas + v2s32(j*dim.Width,0),
607 core::rect<s32>(v2s32(0,0), dim),
608 video::SColor(255,255,255,255),
612 // Copy the borders a few times to disallow texture bleeding
613 for(u32 side=0; side<2; side++) // top and bottom
614 for(s32 y0=0; y0<padding; y0++)
615 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
621 dst_y = y0 + pos_in_atlas.Y + dim.Height;
622 src_y = pos_in_atlas.Y + dim.Height - 1;
626 dst_y = -y0 + pos_in_atlas.Y-1;
627 src_y = pos_in_atlas.Y;
629 s32 x = x0 + pos_in_atlas.X * dim.Width;
630 video::SColor c = atlas_img->getPixel(x, src_y);
631 atlas_img->setPixel(x,dst_y,c);
637 Add texture to caches
641 u32 id = m_atlaspointer_cache.size();
643 // Create AtlasPointer
645 ap.atlas = NULL; // Set on the second pass
646 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
647 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
648 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
649 (float)dim.Width/(float)atlas_dim.Height);
650 ap.tiled = xwise_tiling;
652 // Create SourceAtlasPointer and add to containers
653 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
654 m_atlaspointer_cache.push_back(nap);
655 m_name_to_id.insert(name, id);
657 // Increment position
658 pos_in_atlas.Y += dim.Height + padding * 2;
664 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
668 Second pass: set texture pointer in generated AtlasPointers
670 for(u32 i=0; i<sourcelist.size(); i++)
672 std::string name = sourcelist[i];
673 if(m_name_to_id.find(name) == NULL)
675 u32 id = m_name_to_id[name];
676 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
677 m_atlaspointer_cache[id].a.atlas = t;
681 Write image to file so that it can be inspected
683 /*driver->writeImageToFile(atlas_img,
684 getTexturePath("main_atlas.png").c_str());*/
687 video::IImage* generate_image_from_scratch(std::string name,
688 IrrlichtDevice *device)
690 /*dstream<<"INFO: generate_image_from_scratch(): "
691 "\""<<name<<"\""<<std::endl;*/
693 video::IVideoDriver* driver = device->getVideoDriver();
700 video::IImage *baseimg = NULL;
702 char separator = '^';
704 // Find last meta separator in name
705 s32 last_separator_position = -1;
706 for(s32 i=name.size()-1; i>=0; i--)
708 if(name[i] == separator)
710 last_separator_position = i;
715 /*dstream<<"INFO: generate_image_from_scratch(): "
716 <<"last_separator_position="<<last_separator_position
720 If separator was found, construct the base name and make the
721 base image using a recursive call
723 std::string base_image_name;
724 if(last_separator_position != -1)
726 // Construct base name
727 base_image_name = name.substr(0, last_separator_position);
728 /*dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
729 " to get base image of \""<<name<<"\" = \""
730 <<base_image_name<<"\""<<std::endl;*/
731 baseimg = generate_image_from_scratch(base_image_name, device);
735 Parse out the last part of the name of the image and act
739 std::string last_part_of_name = name.substr(last_separator_position+1);
740 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
742 // Generate image according to part of name
743 if(generate_image(last_part_of_name, baseimg, device) == false)
745 dstream<<"INFO: generate_image_from_scratch(): "
746 "failed to generate \""<<last_part_of_name<<"\""
754 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
755 IrrlichtDevice *device)
757 video::IVideoDriver* driver = device->getVideoDriver();
760 // Stuff starting with [ are special commands
761 if(part_of_name[0] != '[')
763 // A normal texture; load it from a file
764 std::string path = getTexturePath(part_of_name.c_str());
765 /*dstream<<"INFO: generate_image(): Loading path \""<<path
768 video::IImage *image = driver->createImageFromFile(path.c_str());
772 dstream<<"WARNING: generate_image(): Could not load image \""
773 <<part_of_name<<"\" from path \""<<path<<"\""
774 <<" while building texture"<<std::endl;
778 dstream<<"WARNING: generate_image(): Creating a dummy"
779 <<" image for \""<<part_of_name<<"\""<<std::endl;
781 // Just create a dummy image
782 //core::dimension2d<u32> dim(2,2);
783 core::dimension2d<u32> dim(1,1);
784 image = driver->createImage(video::ECF_A8R8G8B8, dim);
786 /*image->setPixel(0,0, video::SColor(255,255,0,0));
787 image->setPixel(1,0, video::SColor(255,0,255,0));
788 image->setPixel(0,1, video::SColor(255,0,0,255));
789 image->setPixel(1,1, video::SColor(255,255,0,255));*/
790 image->setPixel(0,0, video::SColor(255,myrand()%256,
791 myrand()%256,myrand()%256));
792 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
793 myrand()%256,myrand()%256));
794 image->setPixel(0,1, video::SColor(255,myrand()%256,
795 myrand()%256,myrand()%256));
796 image->setPixel(1,1, video::SColor(255,myrand()%256,
797 myrand()%256,myrand()%256));*/
800 // If base image is NULL, load as base.
803 //dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
805 Copy it this way to get an alpha channel.
806 Otherwise images with alpha cannot be blitted on
807 images that don't have alpha in the original file.
809 core::dimension2d<u32> dim = image->getDimension();
810 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
811 image->copyTo(baseimg);
814 // Else blit on base.
817 //dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
818 // Size of the copied area
819 core::dimension2d<u32> dim = image->getDimension();
820 //core::dimension2d<u32> dim(16,16);
821 // Position to copy the blitted to in the base image
822 core::position2d<s32> pos_to(0,0);
823 // Position to copy the blitted from in the blitted image
824 core::position2d<s32> pos_from(0,0);
826 image->copyToWithAlpha(baseimg, pos_to,
827 core::rect<s32>(pos_from, dim),
828 video::SColor(255,255,255,255),
836 // A special texture modification
838 dstream<<"INFO: generate_image(): generating special "
839 <<"modification \""<<part_of_name<<"\""
843 This is the simplest of all; it just adds stuff to the
844 name so that a separate texture is created.
846 It is used to make textures for stuff that doesn't want
847 to implement getting the texture from a bigger texture
850 if(part_of_name == "[forcesingle")
855 Adds a cracking texture
857 else if(part_of_name.substr(0,6) == "[crack")
861 dstream<<"WARNING: generate_image(): baseimg==NULL "
862 <<"for part_of_name=\""<<part_of_name
863 <<"\", cancelling."<<std::endl;
867 // Crack image number
868 u16 progression = stoi(part_of_name.substr(6));
870 // Size of the base image
871 core::dimension2d<u32> dim_base = baseimg->getDimension();
876 It is an image with a number of cracking stages
879 video::IImage *img_crack = driver->createImageFromFile(
880 getTexturePath("crack.png").c_str());
884 // Dimension of original image
885 core::dimension2d<u32> dim_crack
886 = img_crack->getDimension();
887 // Count of crack stages
888 u32 crack_count = dim_crack.Height / dim_crack.Width;
890 if(progression > crack_count-1)
891 progression = crack_count-1;
892 // Dimension of a single scaled crack stage
893 core::dimension2d<u32> dim_crack_scaled_single(
897 // Dimension of scaled size
898 core::dimension2d<u32> dim_crack_scaled(
899 dim_crack_scaled_single.Width,
900 dim_crack_scaled_single.Height * crack_count
902 // Create scaled crack image
903 video::IImage *img_crack_scaled = driver->createImage(
904 video::ECF_A8R8G8B8, dim_crack_scaled);
907 // Scale crack image by copying
908 img_crack->copyToScaling(img_crack_scaled);
910 // Position to copy the crack from
911 core::position2d<s32> pos_crack_scaled(
913 dim_crack_scaled_single.Height * progression
916 // This tiling does nothing currently but is useful
917 for(u32 y0=0; y0<dim_base.Height
918 / dim_crack_scaled_single.Height; y0++)
919 for(u32 x0=0; x0<dim_base.Width
920 / dim_crack_scaled_single.Width; x0++)
922 // Position to copy the crack to in the base image
923 core::position2d<s32> pos_base(
924 x0*dim_crack_scaled_single.Width,
925 y0*dim_crack_scaled_single.Height
927 // Rectangle to copy the crack from on the scaled image
928 core::rect<s32> rect_crack_scaled(
930 dim_crack_scaled_single
933 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
935 video::SColor(255,255,255,255),
939 img_crack_scaled->drop();
946 [combine:WxH:X,Y=filename:X,Y=filename2
947 Creates a bigger texture from an amount of smaller ones
949 else if(part_of_name.substr(0,8) == "[combine")
951 Strfnd sf(part_of_name);
953 u32 w0 = stoi(sf.next("x"));
954 u32 h0 = stoi(sf.next(":"));
955 dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
956 core::dimension2d<u32> dim(w0,h0);
957 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
958 while(sf.atend() == false)
960 u32 x = stoi(sf.next(","));
961 u32 y = stoi(sf.next("="));
962 std::string filename = sf.next(":");
963 dstream<<"INFO: Adding \""<<filename
964 <<"\" to combined ("<<x<<","<<y<<")"
966 video::IImage *img = driver->createImageFromFile(
967 getTexturePath(filename.c_str()).c_str());
970 core::dimension2d<u32> dim = img->getDimension();
971 dstream<<"INFO: Size "<<dim.Width
972 <<"x"<<dim.Height<<std::endl;
973 core::position2d<s32> pos_base(x, y);
974 video::IImage *img2 =
975 driver->createImage(video::ECF_A8R8G8B8, dim);
978 img2->copyToWithAlpha(baseimg, pos_base,
979 core::rect<s32>(v2s32(0,0), dim),
980 video::SColor(255,255,255,255),
986 dstream<<"WARNING: img==NULL"<<std::endl;
992 Adds a progress bar, 0.0 <= N <= 1.0
994 else if(part_of_name.substr(0,12) == "[progressbar")
998 dstream<<"WARNING: generate_image(): baseimg==NULL "
999 <<"for part_of_name=\""<<part_of_name
1000 <<"\", cancelling."<<std::endl;
1004 float value = stof(part_of_name.substr(12));
1005 make_progressbar(value, baseimg);
1008 "[noalpha:filename.png"
1009 Use an image without it's alpha channel.
1010 Used for the leaves texture when in old leaves mode, so
1011 that the transparent parts don't look completely black
1012 when simple alpha channel is used for rendering.
1014 else if(part_of_name.substr(0,8) == "[noalpha")
1018 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1019 <<"for part_of_name=\""<<part_of_name
1020 <<"\", cancelling."<<std::endl;
1024 std::string filename = part_of_name.substr(9);
1026 std::string path = getTexturePath(filename.c_str());
1028 dstream<<"INFO: generate_image(): Loading path \""<<path
1031 video::IImage *image = driver->createImageFromFile(path.c_str());
1035 dstream<<"WARNING: generate_image(): Loading path \""
1036 <<path<<"\" failed"<<std::endl;
1040 core::dimension2d<u32> dim = image->getDimension();
1041 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1043 // Set alpha to full
1044 for(u32 y=0; y<dim.Height; y++)
1045 for(u32 x=0; x<dim.Width; x++)
1047 video::SColor c = image->getPixel(x,y);
1049 image->setPixel(x,y,c);
1052 image->copyTo(baseimg);
1058 "[makealpha:R,G,B:filename.png"
1059 Use an image with converting one color to transparent.
1061 else if(part_of_name.substr(0,11) == "[makealpha:")
1065 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1066 <<"for part_of_name=\""<<part_of_name
1067 <<"\", cancelling."<<std::endl;
1071 Strfnd sf(part_of_name.substr(11));
1072 u32 r1 = stoi(sf.next(","));
1073 u32 g1 = stoi(sf.next(","));
1074 u32 b1 = stoi(sf.next(":"));
1075 std::string filename = sf.next("");
1077 std::string path = getTexturePath(filename.c_str());
1079 dstream<<"INFO: generate_image(): Loading path \""<<path
1082 video::IImage *image = driver->createImageFromFile(path.c_str());
1086 dstream<<"WARNING: generate_image(): Loading path \""
1087 <<path<<"\" failed"<<std::endl;
1091 core::dimension2d<u32> dim = image->getDimension();
1092 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1094 for(u32 y=0; y<dim.Height; y++)
1095 for(u32 x=0; x<dim.Width; x++)
1097 video::SColor c = image->getPixel(x,y);
1099 u32 g = c.getGreen();
1100 u32 b = c.getBlue();
1101 if(!(r == r1 && g == g1 && b == b1))
1104 image->setPixel(x,y,c);
1107 image->copyTo(baseimg);
1113 "[makealpha2:R,G,B;R2,G2,B2:filename.png"
1114 Use an image with converting two colors to transparent.
1116 else if(part_of_name.substr(0,12) == "[makealpha2:")
1120 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1121 <<"for part_of_name=\""<<part_of_name
1122 <<"\", cancelling."<<std::endl;
1126 Strfnd sf(part_of_name.substr(12));
1127 u32 r1 = stoi(sf.next(","));
1128 u32 g1 = stoi(sf.next(","));
1129 u32 b1 = stoi(sf.next(";"));
1130 u32 r2 = stoi(sf.next(","));
1131 u32 g2 = stoi(sf.next(","));
1132 u32 b2 = stoi(sf.next(":"));
1133 std::string filename = sf.next("");
1135 std::string path = getTexturePath(filename.c_str());
1137 dstream<<"INFO: generate_image(): Loading path \""<<path
1140 video::IImage *image = driver->createImageFromFile(path.c_str());
1144 dstream<<"WARNING: generate_image(): Loading path \""
1145 <<path<<"\" failed"<<std::endl;
1149 core::dimension2d<u32> dim = image->getDimension();
1150 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1152 for(u32 y=0; y<dim.Height; y++)
1153 for(u32 x=0; x<dim.Width; x++)
1155 video::SColor c = image->getPixel(x,y);
1157 u32 g = c.getGreen();
1158 u32 b = c.getBlue();
1159 if(!(r == r1 && g == g1 && b == b1) &&
1160 !(r == r2 && g == g2 && b == b2))
1163 image->setPixel(x,y,c);
1166 image->copyTo(baseimg);
1172 [inventorycube{topimage{leftimage{rightimage
1173 In every subimage, replace ^ with &.
1174 Create an "inventory cube".
1175 NOTE: This should be used only on its own.
1176 Example (a grass block (not actually used in game):
1177 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1179 else if(part_of_name.substr(0,14) == "[inventorycube")
1183 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1184 <<"for part_of_name=\""<<part_of_name
1185 <<"\", cancelling."<<std::endl;
1189 str_replace_char(part_of_name, '&', '^');
1190 Strfnd sf(part_of_name);
1192 std::string imagename_top = sf.next("{");
1193 std::string imagename_left = sf.next("{");
1194 std::string imagename_right = sf.next("{");
1199 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1201 dstream<<"WARNING: generate_image(): EVDF_RENDER_TO_TARGET"
1202 " not supported. Creating fallback image"<<std::endl;
1203 baseimg = generate_image_from_scratch(
1204 imagename_top, device);
1210 //dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1211 core::dimension2d<u32> dim(w0,h0);
1213 // Generate images for the faces of the cube
1214 video::IImage *img_top = generate_image_from_scratch(
1215 imagename_top, device);
1216 video::IImage *img_left = generate_image_from_scratch(
1217 imagename_left, device);
1218 video::IImage *img_right = generate_image_from_scratch(
1219 imagename_right, device);
1220 assert(img_top && img_left && img_right);
1222 // TODO: Create textures from images
1223 video::ITexture *texture_top = driver->addTexture(
1224 (imagename_top + "__temp__").c_str(), img_top);
1225 assert(texture_top);
1232 // Create render target texture
1233 video::ITexture *rtt = NULL;
1234 std::string rtt_name = part_of_name + "_RTT";
1235 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1236 video::ECF_A8R8G8B8);
1239 // Set render target
1240 driver->setRenderTarget(rtt, true, true,
1241 video::SColor(0,0,0,0));
1243 // Get a scene manager
1244 scene::ISceneManager *smgr_main = device->getSceneManager();
1246 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1251 - An unit cube is centered at 0,0,0
1252 - Camera looks at cube from Y+, Z- towards Y-, Z+
1253 NOTE: Cube has to be changed to something else because
1254 the textures cannot be set individually (or can they?)
1257 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1258 v3f(0,0,0), v3f(0, 45, 0));
1259 // Set texture of cube
1260 cube->setMaterialTexture(0, texture_top);
1261 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1262 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1263 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1265 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1266 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1267 // Set orthogonal projection
1268 core::CMatrix4<f32> pm;
1269 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1270 camera->setProjectionMatrix(pm, true);
1272 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1273 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1275 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1278 driver->beginScene(true, true, video::SColor(0,0,0,0));
1282 // NOTE: The scene nodes should not be dropped, otherwise
1283 // smgr->drop() segfaults
1287 // Drop scene manager
1290 // Unset render target
1291 driver->setRenderTarget(0, true, true, 0);
1293 //TODO: Free textures of images
1294 driver->removeTexture(texture_top);
1296 // Create image of render target
1297 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1301 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1305 image->copyTo(baseimg);
1312 dstream<<"WARNING: generate_image(): Invalid "
1313 " modification: \""<<part_of_name<<"\""<<std::endl;
1320 void make_progressbar(float value, video::IImage *image)
1325 core::dimension2d<u32> size = image->getDimension();
1327 u32 barheight = size.Height/16;
1328 u32 barpad_x = size.Width/16;
1329 u32 barpad_y = size.Height/16;
1330 u32 barwidth = size.Width - barpad_x*2;
1331 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1333 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1335 video::SColor active(255,255,0,0);
1336 video::SColor inactive(255,0,0,0);
1337 for(u32 x0=0; x0<barwidth; x0++)
1344 u32 x = x0 + barpos.X;
1345 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1347 image->setPixel(x,y, *c);