3 Copyright (C) 2010 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 Replaces the filename extension.
28 std::string image = "a/image.png"
29 replace_ext(image, "jpg")
30 -> image = "a/image.jpg"
31 Returns true on success.
33 inline bool replace_ext(std::string &path, const char *ext)
37 // Find place of last dot, fail if \ or / found.
39 for(s32 i=path.size()-1; i>=0; i--)
47 if(path[i] == '\\' || path[i] == '/')
50 // If not found, return an empty string
53 // Else make the new path
54 path = path.substr(0, last_dot_i+1) + ext;
59 Find out the full path of an image by trying different filename
64 inline std::string getImagePath(std::string path)
66 // A NULL-ended list of possible image extensions
67 const char *extensions[] = {
68 "png", "jpg", "bmp", "tga",
69 "pcx", "ppm", "psd", "wal", "rgb",
73 const char **ext = extensions;
75 bool r = replace_ext(path, *ext);
78 if(fs::PathExists(path))
81 while((++ext) != NULL);
87 Gets the path to a texture by first checking if the texture exists
88 in texture_path and if not, using the data path.
90 inline std::string getTexturePath(std::string filename)
92 std::string texture_path = g_settings.get("texture_path");
93 if(texture_path != "")
95 std::string fullpath = texture_path + '/' + filename;
96 // Check all filename extensions
97 fullpath = getImagePath(fullpath);
98 // If found, return it
102 std::string fullpath = porting::getDataPath(filename.c_str());
103 // Check all filename extensions
104 fullpath = getImagePath(fullpath);
108 TextureSource::TextureSource(IrrlichtDevice *device):
110 m_main_atlas_image(NULL),
111 m_main_atlas_texture(NULL)
115 m_atlaspointer_cache_mutex.Init();
117 m_main_thread = get_current_thread_id();
119 // Add a NULL AtlasPointer as the first index, named ""
120 m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
121 m_name_to_id[""] = 0;
123 // Build main texture atlas
124 if(g_settings.getBool("enable_texture_atlas"))
127 dstream<<"INFO: Not building texture atlas."<<std::endl;
130 TextureSource::~TextureSource()
134 void TextureSource::processQueue()
139 if(m_get_texture_queue.size() > 0)
141 GetRequest<std::string, u32, u8, u8>
142 request = m_get_texture_queue.pop();
144 dstream<<"INFO: TextureSource::processQueue(): "
145 <<"got texture request with "
146 <<"name="<<request.key
149 GetResult<std::string, u32, u8, u8>
151 result.key = request.key;
152 result.callers = request.callers;
153 result.item = getTextureIdDirect(request.key);
155 request.dest->push_back(result);
159 u32 TextureSource::getTextureId(const std::string &name)
161 //dstream<<"INFO: getTextureId(): name="<<name<<std::endl;
165 See if texture already exists
167 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
168 core::map<std::string, u32>::Node *n;
169 n = m_name_to_id.find(name);
172 return n->getValue();
179 if(get_current_thread_id() == m_main_thread)
181 return getTextureIdDirect(name);
185 dstream<<"INFO: getTextureId(): Queued: name="<<name<<std::endl;
187 // We're gonna ask the result to be put into here
188 ResultQueue<std::string, u32, u8, u8> result_queue;
190 // Throw a request in
191 m_get_texture_queue.add(name, 0, 0, &result_queue);
193 dstream<<"INFO: Waiting for texture from main thread, name="
198 // Wait result for a second
199 GetResult<std::string, u32, u8, u8>
200 result = result_queue.pop_front(1000);
202 // Check that at least something worked OK
203 assert(result.key == name);
207 catch(ItemNotFoundException &e)
209 dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
214 dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
219 // Draw a progress bar on the image
220 void make_progressbar(float value, video::IImage *image);
223 Generate image based on a string like "stone.png" or "[crack0".
224 if baseimg is NULL, it is created. Otherwise stuff is made on it.
226 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
227 IrrlichtDevice *device);
230 Generates an image from a full string like
231 "stone.png^mineral_coal.png^[crack0".
233 This is used by buildMainAtlas().
235 video::IImage* generate_image_from_scratch(std::string name,
236 IrrlichtDevice *device);
239 This method generates all the textures
241 u32 TextureSource::getTextureIdDirect(const std::string &name)
243 dstream<<"INFO: getTextureIdDirect(): name="<<name<<std::endl;
245 // Empty name means texture 0
248 dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
253 Calling only allowed from main thread
255 if(get_current_thread_id() != m_main_thread)
257 dstream<<"ERROR: TextureSource::getTextureIdDirect() "
258 "called not from main thread"<<std::endl;
263 See if texture already exists
266 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
268 core::map<std::string, u32>::Node *n;
269 n = m_name_to_id.find(name);
272 dstream<<"INFO: getTextureIdDirect(): name="<<name
273 <<" found in cache"<<std::endl;
274 return n->getValue();
278 dstream<<"INFO: getTextureIdDirect(): name="<<name
279 <<" NOT found in cache. Creating it."<<std::endl;
285 char separator = '^';
288 This is set to the id of the base image.
289 If left 0, there is no base image and a completely new image
292 u32 base_image_id = 0;
294 // Find last meta separator in name
295 s32 last_separator_position = -1;
296 for(s32 i=name.size()-1; i>=0; i--)
298 if(name[i] == separator)
300 last_separator_position = i;
305 If separator was found, construct the base name and make the
306 base image using a recursive call
308 std::string base_image_name;
309 if(last_separator_position != -1)
311 // Construct base name
312 base_image_name = name.substr(0, last_separator_position);
313 dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
314 " to get base image, name="<<base_image_name<<std::endl;
315 base_image_id = getTextureIdDirect(base_image_name);
318 dstream<<"base_image_id="<<base_image_id<<std::endl;
320 video::IVideoDriver* driver = m_device->getVideoDriver();
323 video::ITexture *t = NULL;
326 An image will be built from files and then converted into a texture.
328 video::IImage *baseimg = NULL;
330 // If a base image was found, copy it to baseimg
331 if(base_image_id != 0)
333 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
335 SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
337 video::IImage *image = ap.atlas_img;
341 dstream<<"WARNING: getTextureIdDirect(): NULL image in "
342 <<"cache: \""<<base_image_name<<"\""
347 core::dimension2d<u32> dim = ap.intsize;
349 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
351 core::position2d<s32> pos_to(0,0);
352 core::position2d<s32> pos_from = ap.intpos;
356 v2s32(0,0), // position in target
357 core::rect<s32>(pos_from, dim) // from
360 dstream<<"INFO: getTextureIdDirect(): Loaded \""
361 <<base_image_name<<"\" from image cache"
367 Parse out the last part of the name of the image and act
371 std::string last_part_of_name = name.substr(last_separator_position+1);
372 dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
374 // Generate image according to part of name
375 if(generate_image(last_part_of_name, baseimg, m_device) == false)
377 dstream<<"INFO: getTextureIdDirect(): "
378 "failed to generate \""<<last_part_of_name<<"\""
382 // If no resulting image, print a warning
385 dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
386 " create texture \""<<name<<"\""<<std::endl;
391 // Create texture from resulting image
392 t = driver->addTexture(name.c_str(), baseimg);
396 Add texture to caches (add NULL textures too)
399 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
401 u32 id = m_atlaspointer_cache.size();
407 core::dimension2d<u32> baseimg_dim(0,0);
409 baseimg_dim = baseimg->getDimension();
410 SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim);
411 m_atlaspointer_cache.push_back(nap);
412 m_name_to_id.insert(name, id);
414 dstream<<"INFO: getTextureIdDirect(): name="<<name
415 <<": succesfully returning id="<<id<<std::endl;
420 std::string TextureSource::getTextureName(u32 id)
422 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
424 if(id >= m_atlaspointer_cache.size())
426 dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
427 <<" >= m_atlaspointer_cache.size()="
428 <<m_atlaspointer_cache.size()<<std::endl;
432 return m_atlaspointer_cache[id].name;
436 AtlasPointer TextureSource::getTexture(u32 id)
438 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
440 if(id >= m_atlaspointer_cache.size())
441 return AtlasPointer(0, NULL);
443 return m_atlaspointer_cache[id].a;
446 void TextureSource::buildMainAtlas()
448 dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
450 //return; // Disable (for testing)
452 video::IVideoDriver* driver = m_device->getVideoDriver();
455 JMutexAutoLock lock(m_atlaspointer_cache_mutex);
457 // Create an image of the right size
458 core::dimension2d<u32> atlas_dim(1024,1024);
459 video::IImage *atlas_img =
460 driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
464 A list of stuff to add. This should contain as much of the
465 stuff shown in game as possible, to minimize texture changes.
468 core::array<std::string> sourcelist;
470 sourcelist.push_back("stone.png");
471 sourcelist.push_back("mud.png");
472 sourcelist.push_back("sand.png");
473 sourcelist.push_back("grass.png");
474 sourcelist.push_back("grass_footsteps.png");
475 sourcelist.push_back("tree.png");
476 sourcelist.push_back("tree_top.png");
477 sourcelist.push_back("water.png");
478 sourcelist.push_back("leaves.png");
479 sourcelist.push_back("mud.png^grass_side.png");
481 sourcelist.push_back("stone.png^mineral_coal.png");
482 sourcelist.push_back("stone.png^mineral_iron.png");
483 sourcelist.push_back("mud.png^mineral_coal.png");
484 sourcelist.push_back("mud.png^mineral_iron.png");
485 sourcelist.push_back("sand.png^mineral_coal.png");
486 sourcelist.push_back("sand.png^mineral_iron.png");
488 // Padding to disallow texture bleeding
492 First pass: generate almost everything
494 core::position2d<s32> pos_in_atlas(0,0);
496 pos_in_atlas.Y += padding;
498 for(u32 i=0; i<sourcelist.size(); i++)
500 std::string name = sourcelist[i];
502 /*video::IImage *img = driver->createImageFromFile(
503 getTexturePath(name.c_str()).c_str());
507 core::dimension2d<u32> dim = img->getDimension();
508 // Make a copy with the right color format
509 video::IImage *img2 =
510 driver->createImage(video::ECF_A8R8G8B8, dim);
514 // Generate image by name
515 video::IImage *img2 = generate_image_from_scratch(name, m_device);
518 dstream<<"WARNING: TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
522 core::dimension2d<u32> dim = img2->getDimension();
524 // Don't add to atlas if image is large
525 core::dimension2d<u32> max_size_in_atlas(32,32);
526 if(dim.Width > max_size_in_atlas.Width
527 || dim.Height > max_size_in_atlas.Height)
529 dstream<<"INFO: TextureSource::buildMainAtlas(): Not adding "
530 <<"\""<<name<<"\" because image is large"<<std::endl;
534 // Stop making atlas if atlas is full
535 if(pos_in_atlas.Y + dim.Height > atlas_dim.Height)
537 dstream<<"WARNING: TextureSource::buildMainAtlas(): "
538 <<"Atlas is full, not adding more textures."
543 // Tile it a few times in the X direction
544 u16 xwise_tiling = 16;
545 for(u32 j=0; j<xwise_tiling; j++)
547 // Copy the copy to the atlas
548 img2->copyToWithAlpha(atlas_img,
549 pos_in_atlas + v2s32(j*dim.Width,0),
550 core::rect<s32>(v2s32(0,0), dim),
551 video::SColor(255,255,255,255),
555 // Copy the borders a few times to disallow texture bleeding
556 for(u32 side=0; side<2; side++) // top and bottom
557 for(s32 y0=0; y0<padding; y0++)
558 for(s32 x0=0; x0<(s32)xwise_tiling*(s32)dim.Width; x0++)
564 dst_y = y0 + pos_in_atlas.Y + dim.Height;
565 src_y = pos_in_atlas.Y + dim.Height - 1;
569 dst_y = -y0 + pos_in_atlas.Y-1;
570 src_y = pos_in_atlas.Y;
572 s32 x = x0 + pos_in_atlas.X * dim.Width;
573 video::SColor c = atlas_img->getPixel(x, src_y);
574 atlas_img->setPixel(x,dst_y,c);
580 Add texture to caches
584 u32 id = m_atlaspointer_cache.size();
586 // Create AtlasPointer
588 ap.atlas = NULL; // Set on the second pass
589 ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
590 (float)pos_in_atlas.Y/(float)atlas_dim.Height);
591 ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
592 (float)dim.Width/(float)atlas_dim.Height);
593 ap.tiled = xwise_tiling;
595 // Create SourceAtlasPointer and add to containers
596 SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
597 m_atlaspointer_cache.push_back(nap);
598 m_name_to_id.insert(name, id);
600 // Increment position
601 pos_in_atlas.Y += dim.Height + padding * 2;
607 video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
611 Second pass: set texture pointer in generated AtlasPointers
613 for(u32 i=0; i<sourcelist.size(); i++)
615 std::string name = sourcelist[i];
616 if(m_name_to_id.find(name) == NULL)
618 u32 id = m_name_to_id[name];
619 //dstream<<"id of name "<<name<<" is "<<id<<std::endl;
620 m_atlaspointer_cache[id].a.atlas = t;
624 Write image to file so that it can be inspected
626 /*driver->writeImageToFile(atlas_img,
627 getTexturePath("main_atlas.png").c_str());*/
630 video::IImage* generate_image_from_scratch(std::string name,
631 IrrlichtDevice *device)
633 dstream<<"INFO: generate_image_from_scratch(): "
634 "name="<<name<<std::endl;
636 video::IVideoDriver* driver = device->getVideoDriver();
643 video::IImage *baseimg = NULL;
645 char separator = '^';
647 // Find last meta separator in name
648 s32 last_separator_position = -1;
649 for(s32 i=name.size()-1; i>=0; i--)
651 if(name[i] == separator)
653 last_separator_position = i;
658 /*dstream<<"INFO: generate_image_from_scratch(): "
659 <<"last_separator_position="<<last_separator_position
663 If separator was found, construct the base name and make the
664 base image using a recursive call
666 std::string base_image_name;
667 if(last_separator_position != -1)
669 // Construct base name
670 base_image_name = name.substr(0, last_separator_position);
671 dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
672 " to get base image, name="<<base_image_name<<std::endl;
673 baseimg = generate_image_from_scratch(base_image_name, device);
677 Parse out the last part of the name of the image and act
681 std::string last_part_of_name = name.substr(last_separator_position+1);
682 dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
684 // Generate image according to part of name
685 if(generate_image(last_part_of_name, baseimg, device) == false)
687 dstream<<"INFO: generate_image_from_scratch(): "
688 "failed to generate \""<<last_part_of_name<<"\""
696 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
697 IrrlichtDevice *device)
699 video::IVideoDriver* driver = device->getVideoDriver();
702 // Stuff starting with [ are special commands
703 if(part_of_name[0] != '[')
705 // A normal texture; load it from a file
706 std::string path = getTexturePath(part_of_name.c_str());
707 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
710 video::IImage *image = driver->createImageFromFile(path.c_str());
714 dstream<<"WARNING: Could not load image \""<<part_of_name
715 <<"\" from path \""<<path<<"\""
716 <<" while building texture"<<std::endl;
720 dstream<<"WARNING: Creating a dummy"<<" image for \""
721 <<part_of_name<<"\""<<std::endl;
723 // Just create a dummy image
724 //core::dimension2d<u32> dim(2,2);
725 core::dimension2d<u32> dim(1,1);
726 image = driver->createImage(video::ECF_A8R8G8B8, dim);
728 /*image->setPixel(0,0, video::SColor(255,255,0,0));
729 image->setPixel(1,0, video::SColor(255,0,255,0));
730 image->setPixel(0,1, video::SColor(255,0,0,255));
731 image->setPixel(1,1, video::SColor(255,255,0,255));*/
732 image->setPixel(0,0, video::SColor(255,myrand()%256,
733 myrand()%256,myrand()%256));
734 /*image->setPixel(1,0, video::SColor(255,myrand()%256,
735 myrand()%256,myrand()%256));
736 image->setPixel(0,1, video::SColor(255,myrand()%256,
737 myrand()%256,myrand()%256));
738 image->setPixel(1,1, video::SColor(255,myrand()%256,
739 myrand()%256,myrand()%256));*/
742 // If base image is NULL, load as base.
745 dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
747 Copy it this way to get an alpha channel.
748 Otherwise images with alpha cannot be blitted on
749 images that don't have alpha in the original file.
751 core::dimension2d<u32> dim = image->getDimension();
752 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
753 image->copyTo(baseimg);
756 // Else blit on base.
759 dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
760 // Size of the copied area
761 core::dimension2d<u32> dim = image->getDimension();
762 //core::dimension2d<u32> dim(16,16);
763 // Position to copy the blitted to in the base image
764 core::position2d<s32> pos_to(0,0);
765 // Position to copy the blitted from in the blitted image
766 core::position2d<s32> pos_from(0,0);
768 image->copyToWithAlpha(baseimg, pos_to,
769 core::rect<s32>(pos_from, dim),
770 video::SColor(255,255,255,255),
778 // A special texture modification
780 dstream<<"INFO: getTextureIdDirect(): generating special "
781 <<"modification \""<<part_of_name<<"\""
785 This is the simplest of all; it just adds stuff to the
786 name so that a separate texture is created.
788 It is used to make textures for stuff that doesn't want
789 to implement getting the texture from a bigger texture
792 if(part_of_name == "[forcesingle")
797 Adds a cracking texture
799 else if(part_of_name.substr(0,6) == "[crack")
803 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
804 <<"for part_of_name="<<part_of_name
805 <<", cancelling."<<std::endl;
809 // Crack image number
810 u16 progression = stoi(part_of_name.substr(6));
812 // Size of the base image
813 core::dimension2d<u32> dim_base = baseimg->getDimension();
818 It is an image with a number of cracking stages
821 video::IImage *img_crack = driver->createImageFromFile(
822 getTexturePath("crack.png").c_str());
826 // Dimension of original image
827 core::dimension2d<u32> dim_crack
828 = img_crack->getDimension();
829 // Count of crack stages
830 u32 crack_count = dim_crack.Height / dim_crack.Width;
832 if(progression > crack_count-1)
833 progression = crack_count-1;
834 // Dimension of a single scaled crack stage
835 core::dimension2d<u32> dim_crack_scaled_single(
839 // Dimension of scaled size
840 core::dimension2d<u32> dim_crack_scaled(
841 dim_crack_scaled_single.Width,
842 dim_crack_scaled_single.Height * crack_count
844 // Create scaled crack image
845 video::IImage *img_crack_scaled = driver->createImage(
846 video::ECF_A8R8G8B8, dim_crack_scaled);
849 // Scale crack image by copying
850 img_crack->copyToScaling(img_crack_scaled);
852 // Position to copy the crack from
853 core::position2d<s32> pos_crack_scaled(
855 dim_crack_scaled_single.Height * progression
858 // This tiling does nothing currently but is useful
859 for(u32 y0=0; y0<dim_base.Height
860 / dim_crack_scaled_single.Height; y0++)
861 for(u32 x0=0; x0<dim_base.Width
862 / dim_crack_scaled_single.Width; x0++)
864 // Position to copy the crack to in the base image
865 core::position2d<s32> pos_base(
866 x0*dim_crack_scaled_single.Width,
867 y0*dim_crack_scaled_single.Height
869 // Rectangle to copy the crack from on the scaled image
870 core::rect<s32> rect_crack_scaled(
872 dim_crack_scaled_single
875 img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
877 video::SColor(255,255,255,255),
881 img_crack_scaled->drop();
888 [combine:WxH:X,Y=filename:X,Y=filename2
889 Creates a bigger texture from an amount of smaller ones
891 else if(part_of_name.substr(0,8) == "[combine")
893 Strfnd sf(part_of_name);
895 u32 w0 = stoi(sf.next("x"));
896 u32 h0 = stoi(sf.next(":"));
897 dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
898 core::dimension2d<u32> dim(w0,h0);
899 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
900 while(sf.atend() == false)
902 u32 x = stoi(sf.next(","));
903 u32 y = stoi(sf.next("="));
904 std::string filename = sf.next(":");
905 dstream<<"INFO: Adding \""<<filename
906 <<"\" to combined ("<<x<<","<<y<<")"
908 video::IImage *img = driver->createImageFromFile(
909 getTexturePath(filename.c_str()).c_str());
912 core::dimension2d<u32> dim = img->getDimension();
913 dstream<<"INFO: Size "<<dim.Width
914 <<"x"<<dim.Height<<std::endl;
915 core::position2d<s32> pos_base(x, y);
916 video::IImage *img2 =
917 driver->createImage(video::ECF_A8R8G8B8, dim);
920 img2->copyToWithAlpha(baseimg, pos_base,
921 core::rect<s32>(v2s32(0,0), dim),
922 video::SColor(255,255,255,255),
928 dstream<<"WARNING: img==NULL"<<std::endl;
934 Adds a progress bar, 0.0 <= N <= 1.0
936 else if(part_of_name.substr(0,12) == "[progressbar")
940 dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
941 <<"for part_of_name="<<part_of_name
942 <<", cancelling."<<std::endl;
946 float value = stof(part_of_name.substr(12));
947 make_progressbar(value, baseimg);
950 "[noalpha:filename.png"
951 Use an image without it's alpha channel.
952 Used for the leaves texture when in old leaves mode, so
953 that the transparent parts don't look completely black
954 when simple alpha channel is used for rendering.
956 else if(part_of_name.substr(0,8) == "[noalpha")
960 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
961 <<"for part_of_name="<<part_of_name
962 <<", cancelling."<<std::endl;
966 std::string filename = part_of_name.substr(9);
968 std::string path = getTexturePath(filename.c_str());
970 dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
973 video::IImage *image = driver->createImageFromFile(path.c_str());
977 dstream<<"WARNING: getTextureIdDirect(): Loading path \""
978 <<path<<"\" failed"<<std::endl;
982 core::dimension2d<u32> dim = image->getDimension();
983 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
986 for(u32 y=0; y<dim.Height; y++)
987 for(u32 x=0; x<dim.Width; x++)
989 video::SColor c = image->getPixel(x,y);
991 image->setPixel(x,y,c);
994 image->copyTo(baseimg);
1000 [inventorycube{topimage{leftimage{rightimage
1001 In every subimage, replace ^ with &.
1002 Create an "inventory cube".
1003 NOTE: This should be used only on its own.
1004 Example (a grass block (not actually used in game):
1005 "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
1007 else if(part_of_name.substr(0,14) == "[inventorycube")
1011 dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
1012 <<"for part_of_name="<<part_of_name
1013 <<", cancelling."<<std::endl;
1017 str_replace_char(part_of_name, '&', '^');
1018 Strfnd sf(part_of_name);
1020 std::string imagename_top = sf.next("{");
1021 std::string imagename_left = sf.next("{");
1022 std::string imagename_right = sf.next("{");
1027 if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
1029 dstream<<"WARNING: getTextureIdDirect(): EVDF_RENDER_TO_TARGET"
1030 " not supported. Creating fallback image"<<std::endl;
1031 baseimg = generate_image_from_scratch(
1032 imagename_top, device);
1038 dstream<<"INFO: inventorycube w="<<w0<<" h="<<h0<<std::endl;
1039 core::dimension2d<u32> dim(w0,h0);
1041 // Generate images for the faces of the cube
1042 video::IImage *img_top = generate_image_from_scratch(
1043 imagename_top, device);
1044 video::IImage *img_left = generate_image_from_scratch(
1045 imagename_left, device);
1046 video::IImage *img_right = generate_image_from_scratch(
1047 imagename_right, device);
1048 assert(img_top && img_left && img_right);
1050 // TODO: Create textures from images
1051 video::ITexture *texture_top = driver->addTexture(
1052 (imagename_top + "__temp__").c_str(), img_top);
1053 assert(texture_top);
1060 // Create render target texture
1061 video::ITexture *rtt = NULL;
1062 std::string rtt_name = part_of_name + "_RTT";
1063 rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
1064 video::ECF_A8R8G8B8);
1067 // Set render target
1068 driver->setRenderTarget(rtt, true, true,
1069 video::SColor(0,0,0,0));
1071 // Get a scene manager
1072 scene::ISceneManager *smgr_main = device->getSceneManager();
1074 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
1079 - An unit cube is centered at 0,0,0
1080 - Camera looks at cube from Y+, Z- towards Y-, Z+
1081 NOTE: Cube has to be changed to something else because
1082 the textures cannot be set individually (or can they?)
1085 scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
1086 v3f(0,0,0), v3f(0, 45, 0));
1087 // Set texture of cube
1088 cube->setMaterialTexture(0, texture_top);
1089 //cube->setMaterialFlag(video::EMF_LIGHTING, false);
1090 cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
1091 cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
1093 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
1094 v3f(0, 1.0, -1.5), v3f(0, 0, 0));
1095 // Set orthogonal projection
1096 core::CMatrix4<f32> pm;
1097 pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
1098 camera->setProjectionMatrix(pm, true);
1100 /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
1101 v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
1103 smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
1106 driver->beginScene(true, true, video::SColor(0,0,0,0));
1110 // NOTE: The scene nodes should not be dropped, otherwise
1111 // smgr->drop() segfaults
1115 // Drop scene manager
1118 // Unset render target
1119 driver->setRenderTarget(0, true, true, 0);
1121 //TODO: Free textures of images
1122 driver->removeTexture(texture_top);
1124 // Create image of render target
1125 video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
1129 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
1133 image->copyTo(baseimg);
1140 dstream<<"WARNING: getTextureIdDirect(): Invalid "
1141 " modification: \""<<part_of_name<<"\""<<std::endl;
1148 void make_progressbar(float value, video::IImage *image)
1153 core::dimension2d<u32> size = image->getDimension();
1155 u32 barheight = size.Height/16;
1156 u32 barpad_x = size.Width/16;
1157 u32 barpad_y = size.Height/16;
1158 u32 barwidth = size.Width - barpad_x*2;
1159 v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
1161 u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
1163 video::SColor active(255,255,0,0);
1164 video::SColor inactive(255,0,0,0);
1165 for(u32 x0=0; x0<barwidth; x0++)
1172 u32 x = x0 + barpos.X;
1173 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
1175 image->setPixel(x,y, *c);