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
27 A cache from texture name to texture path
29 MutexedMap<std::string, std::string> g_texturename_to_path_cache;
32 Replaces the filename extension.
34 std::string image = "a/image.png"
35 replace_ext(image, "jpg")
36 -> image = "a/image.jpg"
37 Returns true on success.
39 static bool replace_ext(std::string &path, const char *ext)
43 // Find place of last dot, fail if \ or / found.
45 for(s32 i=path.size()-1; i>=0; i--)
53 if(path[i] == '\\' || path[i] == '/')
56 // If not found, return an empty string
59 // Else make the new path
60 path = path.substr(0, last_dot_i+1) + ext;
65 Find out the full path of an image by trying different filename
70 static std::string getImagePath(std::string path)
72 // A NULL-ended list of possible image extensions
73 const char *extensions[] = {
74 "png", "jpg", "bmp", "tga",
75 "pcx", "ppm", "psd", "wal", "rgb",
79 const char **ext = extensions;
81 bool r = replace_ext(path, *ext);
84 if(fs::PathExists(path))
87 while((++ext) != NULL);
93 Gets the path to a texture by first checking if the texture exists
94 in texture_path and if not, using the data path.
96 Checks all supported extensions by replacing the original extension.
98 If not found, returns "".
100 Utilizes a thread-safe cache.
102 std::string getTexturePath(const std::string &filename)
104 std::string fullpath = "";
108 bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
113 Check from texture_path
115 std::string texture_path = g_settings.get("texture_path");
116 if(texture_path != "")
118 std::string testpath = texture_path + '/' + filename;
119 // Check all filename extensions. Returns "" if not found.
120 fullpath = getImagePath(testpath);
124 Check from default data directory
128 std::string testpath = porting::getDataPath(filename.c_str());
129 // Check all filename extensions. Returns "" if not found.
130 fullpath = getImagePath(testpath);
133 // Add to cache (also an empty result is cached)
134 g_texturename_to_path_cache.set(filename, fullpath);
144 TextureSource::TextureSource(IrrlichtDevice *device):
146 m_main_atlas_image(NULL),
147 m_main_atlas_texture(NULL)
151 m_atlaspointer_cache_mutex.Init();
153 m_main_thread = get_current_thread_id();
155 // Add a NULL AtlasPointer as the first index, named ""
156 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
157 m_name_to_id[""] = 0;
159 // Build main texture atlas
160 if(g_settings.getBool("enable_texture_atlas"))
163 dstream<<"INFO: Not building texture atlas."<<std::endl;
166 TextureSource::~TextureSource()
170 void TextureSource::processQueue()
175 if(m_get_texture_queue.size() > 0)
177 GetRequest<std::string, u32, u8, u8>
178 request = m_get_texture_queue.pop();
180 dstream<<"INFO: TextureSource::processQueue(): "
181 <<"got texture request with "
182 <<"name=\""<<request.key<<"\""
185 GetResult<std::string, u32, u8, u8>
187 result.key = request.key;
188 result.callers = request.callers;
189 result.item = getTextureIdDirect(request.key);
191 request.dest->push_back(result);
195 u32 TextureSource::getTextureId(const std::string &name)
197 //dstream<<"INFO: getTextureId(): \""<<name<<"\""<<std::endl;
201 See if texture already exists
203 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
204 core::map<std::string, u32>::Node *n;
205 n = m_name_to_id.find(name);
208 return n->getValue();
215 if(get_current_thread_id() == m_main_thread)
217 return getTextureIdDirect(name);
221 dstream<<"INFO: getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
223 // We're gonna ask the result to be put into here
224 ResultQueue<std::string, u32, u8, u8> result_queue;
226 // Throw a request in
227 m_get_texture_queue.add(name, 0, 0, &result_queue);
229 dstream<<"INFO: Waiting for texture from main thread, name=\""
230 <<name<<"\""<<std::endl;
234 // Wait result for a second
235 GetResult<std::string, u32, u8, u8>
236 result = result_queue.pop_front(1000);
238 // Check that at least something worked OK
239 assert(result.key == name);
243 catch(ItemNotFoundException &e)
245 dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
250 dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
255 // Draw a progress bar on the image
256 void make_progressbar(float value, video::IImage *image);
259 Generate image based on a string like "stone.png" or "[crack0".
260 if baseimg is NULL, it is created. Otherwise stuff is made on it.
262 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
263 IrrlichtDevice *device);
266 Generates an image from a full string like
267 "stone.png^mineral_coal.png^[crack0".
269 This is used by buildMainAtlas().
271 video::IImage* generate_image_from_scratch(std::string name,
272 IrrlichtDevice *device);
275 This method generates all the textures
277 u32 TextureSource::getTextureIdDirect(const std::string &name)
279 //dstream<<"INFO: getTextureIdDirect(): name=\""<<name<<"\""<<std::endl;
281 // Empty name means texture 0
284 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
289 Calling only allowed from main thread
291 if(get_current_thread_id() != m_main_thread)
293 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
294 "called not from main thread"<<std::endl;
299 See if texture already exists
302 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
304 core::map<std::string, u32>::Node *n;
305 n = m_name_to_id.find(name);
308 dstream<<"INFO: getTextureIdDirect(): \""<<name
309 <<"\" found in cache"<<std::endl;
310 return n->getValue();
314 dstream<<"INFO: getTextureIdDirect(): \""<<name
315 <<"\" NOT found in cache. Creating it."<<std::endl;
321 char separator = '^';
324 This is set to the id of the base image.
325 If left 0, there is no base image and a completely new image
328 u32 base_image_id = 0;
330 // Find last meta separator in name
331 s32 last_separator_position = -1;
332 for(s32 i=name.size()-1; i>=0; i--)
334 if(name[i] == separator)
336 last_separator_position = i;
341 If separator was found, construct the base name and make the
342 base image using a recursive call
344 std::string base_image_name;
345 if(last_separator_position != -1)
347 // Construct base name
348 base_image_name = name.substr(0, last_separator_position);
349 /*dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
350 " to get base image of \""<<name<<"\" = \""
351 <<base_image_name<<"\""<<std::endl;*/
352 base_image_id = getTextureIdDirect(base_image_name);
355 //dstream<<"base_image_id="<<base_image_id<<std::endl;
357 video::IVideoDriver* driver = m_device->getVideoDriver();
360 video::ITexture *t = NULL;
363 An image will be built from files and then converted into a texture.
365 video::IImage *baseimg = NULL;
367 // If a base image was found, copy it to baseimg
368 if(base_image_id != 0)
370 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
372 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
374 video::IImage *image = ap.atlas_img;
378 dstream<<"WARNING: getTextureIdDirect(): NULL image in "
379 <<"cache: \""<<base_image_name<<"\""
384 core::dimension2d<u32> dim = ap.intsize;
386 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
388 core::position2d<s32> pos_to(0,0);
389 core::position2d<s32> pos_from = ap.intpos;
393 v2s32(0,0), // position in target
394 core::rect<s32>(pos_from, dim) // from
397 /*dstream<<"INFO: getTextureIdDirect(): Loaded \""
398 <<base_image_name<<"\" from image cache"
404 Parse out the last part of the name of the image and act
408 std::string last_part_of_name = name.substr(last_separator_position+1);
409 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
411 // Generate image according to part of name
412 if(generate_image(last_part_of_name, baseimg, m_device) == false)
414 dstream<<"INFO: getTextureIdDirect(): "
415 "failed to generate \""<<last_part_of_name<<"\""
419 // If no resulting image, print a warning
422 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
423 " create texture \""<<name<<"\""<<std::endl;
428 // Create texture from resulting image
429 t = driver->addTexture(name.c_str(), baseimg);
433 Add texture to caches (add NULL textures too)
436 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
438 u32 id = m_atlaspointer_cache.size();
444 core::dimension2d<u32> baseimg_dim(0,0);
446 baseimg_dim = baseimg->getDimension();
447 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
448 m_atlaspointer_cache.push_back(nap);
449 m_name_to_id.insert(name, id);
451 /*dstream<<"INFO: getTextureIdDirect(): "
452 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
457 std::string TextureSource::getTextureName(u32 id)
459 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
461 if(id >= m_atlaspointer_cache.size())
463 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
464 <<" >= m_atlaspointer_cache.size()="
465 <<m_atlaspointer_cache.size()<<std::endl;
469 return m_atlaspointer_cache[id].name;
473 AtlasPointer TextureSource::getTexture(u32 id)
475 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
477 if(id >= m_atlaspointer_cache.size())
478 return AtlasPointer(0, NULL);
480 return m_atlaspointer_cache[id].a;
483 void TextureSource::buildMainAtlas()
485 dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
487 //return; // Disable (for testing)
489 video::IVideoDriver* driver = m_device->getVideoDriver();
492 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
494 // Create an image of the right size
495 core::dimension2d<u32> atlas_dim(1024,1024);
496 video::IImage *atlas_img =
497 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
501 A list of stuff to add. This should contain as much of the
502 stuff shown in game as possible, to minimize texture changes.
505 core::array<std::string> sourcelist;
507 sourcelist.push_back("stone.png");
508 sourcelist.push_back("mud.png");
509 sourcelist.push_back("sand.png");
510 sourcelist.push_back("grass.png");
511 sourcelist.push_back("grass_footsteps.png");
512 sourcelist.push_back("tree.png");
513 sourcelist.push_back("tree_top.png");
514 sourcelist.push_back("water.png");
515 sourcelist.push_back("leaves.png");
516 sourcelist.push_back("glass.png");
517 sourcelist.push_back("mud.png^grass_side.png");
518 sourcelist.push_back("cobble.png");
519 sourcelist.push_back("mossycobble.png");
520 sourcelist.push_back("gravel.png");
522 sourcelist.push_back("stone.png^mineral_coal.png");
523 sourcelist.push_back("stone.png^mineral_iron.png");
524 sourcelist.push_back("mud.png^mineral_coal.png");
525 sourcelist.push_back("mud.png^mineral_iron.png");
526 sourcelist.push_back("sand.png^mineral_coal.png");
527 sourcelist.push_back("sand.png^mineral_iron.png");
529 // Padding to disallow texture bleeding
533 First pass: generate almost everything
535 core::position2d<s32> pos_in_atlas(0,0);
537 pos_in_atlas.Y += padding;
539 for(u32 i=0; i<sourcelist.size(); i++)
541 std::string name = sourcelist[i];
543 /*video::IImage *img = driver->createImageFromFile(
544 getTexturePath(name.c_str()).c_str());
548 core::dimension2d<u32> dim = img->getDimension();
549 // Make a copy with the right color format
550 video::IImage *img2 =
551 driver->createImage(video::ECF_A8R8G8B8, dim);
555 // Generate image by name
556 video::IImage *img2 = generate_image_from_scratch(name, m_device);
559 dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
563 core::dimension2d<u32> dim = img2->getDimension();
565 // Don't add to atlas if image is large
566 core::dimension2d<u32> max_size_in_atlas(32,32);
567 if(dim.Width > max_size_in_atlas.Width
568 || dim.Height > max_size_in_atlas.Height)
570 dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
571 <<"\""<<name<<"\" because image is large"<<std::endl;
575 // Stop making atlas if atlas is full
576 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
578 dstream<<"WARNING: TextureSource::buildMainAtlas(): "
579 <<"Atlas is full, not adding more textures."
584 dstream<<"INFO: TextureSource::buildMainAtlas(): Adding \""<<name
585 <<"\" to texture atlas"<<std::endl;
587 // Tile it a few times in the X direction
588 u16 xwise_tiling = 16;
589 for(u32 j=0; j<xwise_tiling; j++)
591 // Copy the copy to the atlas
592 img2->copyToWithAlpha(atlas_img,
593 pos_in_atlas + v2s32(j*dim.Width,0),
594 core::rect<s32>(v2s32(0,0), dim),
595 video::SColor(255,255,255,255),
599 // Copy the borders a few times to disallow texture bleeding
600 for(u32 side=0; side<2; side++) // top and bottom
601 for(s32 y0=0; y0<padding; y0++)
602 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
608 dst_y = y0 + pos_in_atlas.Y + dim.Height;
609 src_y = pos_in_atlas.Y + dim.Height - 1;
613 dst_y = -y0 + pos_in_atlas.Y-1;
614 src_y = pos_in_atlas.Y;
616 s32 x = x0 + pos_in_atlas.X * dim.Width;
617 video::SColor c = atlas_img->getPixel(x, src_y);
618 atlas_img->setPixel(x,dst_y,c);
624 Add texture to caches
628 u32 id = m_atlaspointer_cache.size();
630 // Create AtlasPointer
632 ap.atlas = NULL; // Set on the second pass
633 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
634 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
635 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
636 (float)dim.Width/(float)atlas_dim.Height);
637 ap.tiled = xwise_tiling;
639 // Create SourceAtlasPointer and add to containers
640 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
641 m_atlaspointer_cache.push_back(nap);
642 m_name_to_id.insert(name, id);
644 // Increment position
645 pos_in_atlas.Y += dim.Height + padding * 2;
651 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
655 Second pass: set texture pointer in generated AtlasPointers
657 for(u32 i=0; i<sourcelist.size(); i++)
659 std::string name = sourcelist[i];
660 if(m_name_to_id.find(name) == NULL)
662 u32 id = m_name_to_id[name];
663 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
664 m_atlaspointer_cache[id].a.atlas = t;
668 Write image to file so that it can be inspected
670 /*driver->writeImageToFile(atlas_img,
671 getTexturePath("main_atlas.png").c_str());*/
674 video::IImage* generate_image_from_scratch(std::string name,
675 IrrlichtDevice *device)
677 /*dstream<<"INFO: generate_image_from_scratch(): "
678 "\""<<name<<"\""<<std::endl;*/
680 video::IVideoDriver* driver = device->getVideoDriver();
687 video::IImage *baseimg = NULL;
689 char separator = '^';
691 // Find last meta separator in name
692 s32 last_separator_position = -1;
693 for(s32 i=name.size()-1; i>=0; i--)
695 if(name[i] == separator)
697 last_separator_position = i;
702 /*dstream<<"INFO: generate_image_from_scratch(): "
703 <<"last_separator_position="<<last_separator_position
707 If separator was found, construct the base name and make the
708 base image using a recursive call
710 std::string base_image_name;
711 if(last_separator_position != -1)
713 // Construct base name
714 base_image_name = name.substr(0, last_separator_position);
715 /*dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
716 " to get base image of \""<<name<<"\" = \""
717 <<base_image_name<<"\""<<std::endl;*/
718 baseimg = generate_image_from_scratch(base_image_name, device);
722 Parse out the last part of the name of the image and act
726 std::string last_part_of_name = name.substr(last_separator_position+1);
727 //dstream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
729 // Generate image according to part of name
730 if(generate_image(last_part_of_name, baseimg, device) == false)
732 dstream<<"INFO: generate_image_from_scratch(): "
733 "failed to generate \""<<last_part_of_name<<"\""
741 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
742 IrrlichtDevice *device)
744 video::IVideoDriver* driver = device->getVideoDriver();
747 // Stuff starting with [ are special commands
748 if(part_of_name[0] != '[')
750 // A normal texture; load it from a file
751 std::string path = getTexturePath(part_of_name.c_str());
752 /*dstream<<"INFO: generate_image(): Loading path \""<<path
755 video::IImage *image = driver->createImageFromFile(path.c_str());
759 dstream<<"WARNING: generate_image(): Could not load image \""
760 <<part_of_name<<"\" from path \""<<path<<"\""
761 <<" while building texture"<<std::endl;
765 dstream<<"WARNING: generate_image(): Creating a dummy"
766 <<" image for \""<<part_of_name<<"\""<<std::endl;
768 // Just create a dummy image
769 //core::dimension2d<u32> dim(2,2);
770 core::dimension2d<u32> dim(1,1);
771 image = driver->createImage(video::ECF_A8R8G8B8, dim);
773 /*image->setPixel(0,0, video::SColor(255,255,0,0));
774 image->setPixel(1,0, video::SColor(255,0,255,0));
775 image->setPixel(0,1, video::SColor(255,0,0,255));
776 image->setPixel(1,1, video::SColor(255,255,0,255));*/
777 image->setPixel(0,0, video::SColor(255,myrand()%256,
778 myrand()%256,myrand()%256));
779 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
780 myrand()%256,myrand()%256));
781 image->setPixel(0,1, video::SColor(255,myrand()%256,
782 myrand()%256,myrand()%256));
783 image->setPixel(1,1, video::SColor(255,myrand()%256,
784 myrand()%256,myrand()%256));*/
787 // If base image is NULL, load as base.
790 //dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
792 Copy it this way to get an alpha channel.
793 Otherwise images with alpha cannot be blitted on
794 images that don't have alpha in the original file.
796 core::dimension2d<u32> dim = image->getDimension();
797 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
798 image->copyTo(baseimg);
801 // Else blit on base.
804 //dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
805 // Size of the copied area
806 core::dimension2d<u32> dim = image->getDimension();
807 //core::dimension2d<u32> dim(16,16);
808 // Position to copy the blitted to in the base image
809 core::position2d<s32> pos_to(0,0);
810 // Position to copy the blitted from in the blitted image
811 core::position2d<s32> pos_from(0,0);
813 image->copyToWithAlpha(baseimg, pos_to,
814 core::rect<s32>(pos_from, dim),
815 video::SColor(255,255,255,255),
823 // A special texture modification
825 dstream<<"INFO: generate_image(): generating special "
826 <<"modification \""<<part_of_name<<"\""
830 This is the simplest of all; it just adds stuff to the
831 name so that a separate texture is created.
833 It is used to make textures for stuff that doesn't want
834 to implement getting the texture from a bigger texture
837 if(part_of_name == "[forcesingle")
842 Adds a cracking texture
844 else if(part_of_name.substr(0,6) == "[crack")
848 dstream<<"WARNING: generate_image(): baseimg==NULL "
849 <<"for part_of_name=\""<<part_of_name
850 <<"\", cancelling."<<std::endl;
854 // Crack image number
855 u16 progression = stoi(part_of_name.substr(6));
857 // Size of the base image
858 core::dimension2d<u32> dim_base = baseimg->getDimension();
863 It is an image with a number of cracking stages
866 video::IImage *img_crack = driver->createImageFromFile(
867 getTexturePath("crack.png").c_str());
871 // Dimension of original image
872 core::dimension2d<u32> dim_crack
873 = img_crack->getDimension();
874 // Count of crack stages
875 u32 crack_count = dim_crack.Height / dim_crack.Width;
877 if(progression > crack_count-1)
878 progression = crack_count-1;
879 // Dimension of a single scaled crack stage
880 core::dimension2d<u32> dim_crack_scaled_single(
884 // Dimension of scaled size
885 core::dimension2d<u32> dim_crack_scaled(
886 dim_crack_scaled_single.Width,
887 dim_crack_scaled_single.Height * crack_count
889 // Create scaled crack image
890 video::IImage *img_crack_scaled = driver->createImage(
891 video::ECF_A8R8G8B8, dim_crack_scaled);
894 // Scale crack image by copying
895 img_crack->copyToScaling(img_crack_scaled);
897 // Position to copy the crack from
898 core::position2d<s32> pos_crack_scaled(
900 dim_crack_scaled_single.Height * progression
903 // This tiling does nothing currently but is useful
904 for(u32 y0=0; y0<dim_base.Height
905 / dim_crack_scaled_single.Height; y0++)
906 for(u32 x0=0; x0<dim_base.Width
907 / dim_crack_scaled_single.Width; x0++)
909 // Position to copy the crack to in the base image
910 core::position2d<s32> pos_base(
911 x0*dim_crack_scaled_single.Width,
912 y0*dim_crack_scaled_single.Height
914 // Rectangle to copy the crack from on the scaled image
915 core::rect<s32> rect_crack_scaled(
917 dim_crack_scaled_single
920 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
922 video::SColor(255,255,255,255),
926 img_crack_scaled->drop();
933 [combine:WxH:X,Y=filename:X,Y=filename2
934 Creates a bigger texture from an amount of smaller ones
936 else if(part_of_name.substr(0,8) == "[combine")
938 Strfnd sf(part_of_name);
940 u32 w0 = stoi(sf.next("x"));
941 u32 h0 = stoi(sf.next(":"));
942 dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
943 core::dimension2d<u32> dim(w0,h0);
944 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
945 while(sf.atend() == false)
947 u32 x = stoi(sf.next(","));
948 u32 y = stoi(sf.next("="));
949 std::string filename = sf.next(":");
950 dstream<<"INFO: Adding \""<<filename
951 <<"\" to combined ("<<x<<","<<y<<")"
953 video::IImage *img = driver->createImageFromFile(
954 getTexturePath(filename.c_str()).c_str());
957 core::dimension2d<u32> dim = img->getDimension();
958 dstream<<"INFO: Size "<<dim.Width
959 <<"x"<<dim.Height<<std::endl;
960 core::position2d<s32> pos_base(x, y);
961 video::IImage *img2 =
962 driver->createImage(video::ECF_A8R8G8B8, dim);
965 img2->copyToWithAlpha(baseimg, pos_base,
966 core::rect<s32>(v2s32(0,0), dim),
967 video::SColor(255,255,255,255),
973 dstream<<"WARNING: img==NULL"<<std::endl;
979 Adds a progress bar, 0.0 <= N <= 1.0
981 else if(part_of_name.substr(0,12) == "[progressbar")
985 dstream<<"WARNING: generate_image(): baseimg==NULL "
986 <<"for part_of_name=\""<<part_of_name
987 <<"\", cancelling."<<std::endl;
991 float value = stof(part_of_name.substr(12));
992 make_progressbar(value, baseimg);
995 "[noalpha:filename.png"
996 Use an image without it's alpha channel.
997 Used for the leaves texture when in old leaves mode, so
998 that the transparent parts don't look completely black
999 when simple alpha channel is used for rendering.
1001 else if(part_of_name.substr(0,8) == "[noalpha")
1005 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1006 <<"for part_of_name=\""<<part_of_name
1007 <<"\", cancelling."<<std::endl;
1011 std::string filename = part_of_name.substr(9);
1013 std::string path = getTexturePath(filename.c_str());
1015 dstream<<"INFO: generate_image(): Loading path \""<<path
1018 video::IImage *image = driver->createImageFromFile(path.c_str());
1022 dstream<<"WARNING: generate_image(): Loading path \""
1023 <<path<<"\" failed"<<std::endl;
1027 core::dimension2d<u32> dim = image->getDimension();
1028 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1030 // Set alpha to full
1031 for(u32 y=0; y<dim.Height; y++)
1032 for(u32 x=0; x<dim.Width; x++)
1034 video::SColor c = image->getPixel(x,y);
1036 image->setPixel(x,y,c);
1039 image->copyTo(baseimg);
1045 [inventorycube{topimage{leftimage{rightimage
1046 In every subimage, replace ^ with &.
1047 Create an "inventory cube".
1048 NOTE: This should be used only on its own.
1049 Example (a grass block (not actually used in game):
1050 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1052 else if(part_of_name.substr(0,14) == "[inventorycube")
1056 dstream<<"WARNING: generate_image(): baseimg!=NULL "
1057 <<"for part_of_name=\""<<part_of_name
1058 <<"\", cancelling."<<std::endl;
1062 str_replace_char(part_of_name, '&', '^');
1063 Strfnd sf(part_of_name);
1065 std::string imagename_top = sf.next("{");
1066 std::string imagename_left = sf.next("{");
1067 std::string imagename_right = sf.next("{");
1072 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1074 dstream<<"WARNING: generate_image(): EVDF_RENDER_TO_TARGET"
1075 " not supported. Creating fallback image"<<std::endl;
1076 baseimg = generate_image_from_scratch(
1077 imagename_top, device);
1083 //dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1084 core::dimension2d<u32> dim(w0,h0);
1086 // Generate images for the faces of the cube
1087 video::IImage *img_top = generate_image_from_scratch(
1088 imagename_top, device);
1089 video::IImage *img_left = generate_image_from_scratch(
1090 imagename_left, device);
1091 video::IImage *img_right = generate_image_from_scratch(
1092 imagename_right, device);
1093 assert(img_top && img_left && img_right);
1095 // TODO: Create textures from images
1096 video::ITexture *texture_top = driver->addTexture(
1097 (imagename_top + "__temp__").c_str(), img_top);
1098 assert(texture_top);
1105 // Create render target texture
1106 video::ITexture *rtt = NULL;
1107 std::string rtt_name = part_of_name + "_RTT";
1108 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1109 video::ECF_A8R8G8B8);
1112 // Set render target
1113 driver->setRenderTarget(rtt, true, true,
1114 video::SColor(0,0,0,0));
1116 // Get a scene manager
1117 scene::ISceneManager *smgr_main = device->getSceneManager();
1119 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1124 - An unit cube is centered at 0,0,0
1125 - Camera looks at cube from Y+, Z- towards Y-, Z+
1126 NOTE: Cube has to be changed to something else because
1127 the textures cannot be set individually (or can they?)
1130 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1131 v3f(0,0,0), v3f(0, 45, 0));
1132 // Set texture of cube
1133 cube->setMaterialTexture(0, texture_top);
1134 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1135 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1136 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1138 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1139 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1140 // Set orthogonal projection
1141 core::CMatrix4<f32> pm;
1142 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1143 camera->setProjectionMatrix(pm, true);
1145 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1146 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1148 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1151 driver->beginScene(true, true, video::SColor(0,0,0,0));
1155 // NOTE: The scene nodes should not be dropped, otherwise
1156 // smgr->drop() segfaults
1160 // Drop scene manager
1163 // Unset render target
1164 driver->setRenderTarget(0, true, true, 0);
1166 //TODO: Free textures of images
1167 driver->removeTexture(texture_top);
1169 // Create image of render target
1170 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1174 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1178 image->copyTo(baseimg);
1185 dstream<<"WARNING: generate_image(): Invalid "
1186 " modification: \""<<part_of_name<<"\""<<std::endl;
1193 void make_progressbar(float value, video::IImage *image)
1198 core::dimension2d<u32> size = image->getDimension();
1200 u32 barheight = size.Height/16;
1201 u32 barpad_x = size.Width/16;
1202 u32 barpad_y = size.Height/16;
1203 u32 barwidth = size.Width - barpad_x*2;
1204 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1206 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1208 video::SColor active(255,255,0,0);
1209 video::SColor inactive(255,0,0,0);
1210 for(u32 x0=0; x0<barwidth; x0++)
1217 u32 x = x0 + barpos.X;
1218 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1220 image->setPixel(x,y, *c);