/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tile.h"
#include "debug.h"
#include "main.h" // for g_settings
#include "filesys.h"
#include "utility.h"
#include "settings.h"
#include "mesh.h"
#include <ICameraSceneNode.h>
#include "log.h"
#include "mapnode.h" // For texture atlas making
#include "nodedef.h" // For texture atlas making
#include "gamedef.h"
#include "utility_string.h"
/*
A cache from texture name to texture path
*/
MutexedMap<std::string, std::string> g_texturename_to_path_cache;
/*
Replaces the filename extension.
eg:
std::string image = "a/image.png"
replace_ext(image, "jpg")
-> image = "a/image.jpg"
Returns true on success.
*/
static bool replace_ext(std::string &path, const char *ext)
{
if(ext == NULL)
return false;
// Find place of last dot, fail if \ or / found.
s32 last_dot_i = -1;
for(s32 i=path.size()-1; i>=0; i--)
{
if(path[i] == '.')
{
last_dot_i = i;
break;
}
if(path[i] == '\\' || path[i] == '/')
break;
}
// If not found, return an empty string
if(last_dot_i == -1)
return false;
// Else make the new path
path = path.substr(0, last_dot_i+1) + ext;
return true;
}
/*
Find out the full path of an image by trying different filename
extensions.
If failed, return "".
*/
static std::string getImagePath(std::string path)
{
// A NULL-ended list of possible image extensions
const char *extensions[] = {
"png", "jpg", "bmp", "tga",
"pcx", "ppm", "psd", "wal", "rgb",
NULL
};
// If there is no extension, add one
if(removeStringEnd(path, extensions) == "")
path = path + ".png";
// Check paths until something is found to exist
const char **ext = extensions;
do{
bool r = replace_ext(path, *ext);
if(r == false)
return "";
if(fs::PathExists(path))
return path;
}
while((++ext) != NULL);
return "";
}
/*
Gets the path to a texture by first checking if the texture exists
in texture_path and if not, using the data path.
Checks all supported extensions by replacing the original extension.
If not found, returns "".
Utilizes a thread-safe cache.
*/
std::string getTexturePath(const std::string &filename)
{
std::string fullpath = "";
/*
Check from cache
*/
bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
if(incache)
return fullpath;
/*
Check from texture_path
*/
std::string texture_path = g_settings->get("texture_path");
if(texture_path != "")
{
std::string testpath = texture_path + DIR_DELIM + filename;
// Check all filename extensions. Returns "" if not found.
fullpath = getImagePath(testpath);
}
/*
Check from default data directory
*/
if(fullpath == "")
{
std::string base_path = porting::path_share + DIR_DELIM + "textures"
+ DIR_DELIM + "base" + DIR_DELIM + "pack";
std::string testpath = base_path + DIR_DELIM + filename;
// Check all filename extensions. Returns "" if not found.
fullpath = getImagePath(testpath);
}
// Add to cache (also an empty result is cached)
g_texturename_to_path_cache.set(filename, fullpath);
// Finally return it
return fullpath;
}
/*
An internal variant of AtlasPointer with more data.
(well, more like a wrapper)
*/
struct SourceAtlasPointer
{
std::string name;
AtlasPointer a;
video::IImage *atlas_img; // The source image of the atlas
// Integer variants of position and size
v2s32 intpos;
v2u32 intsize;
SourceAtlasPointer(
const std::string &name_,
AtlasPointer a_=AtlasPointer(0, NULL),
video::IImage *atlas_img_=NULL,
v2s32 intpos_=v2s32(0,0),
v2u32 intsize_=v2u32(0,0)
):
name(name_),
a(a_),
atlas_img(atlas_img_),
intpos(intpos_),
intsize(intsize_)
{
}
};
/*
SourceImageCache: A cache used for storing source images.
*/
class SourceImageCache
{
public:
void insert(const std::string &name, video::IImage *img,
bool prefer_local, video::IVideoDriver *driver)
{
assert(img);
// Remove old image
core::map<std::string, video::IImage*>::Node *n;
n = m_images.find(name);
if(n){
video::IImage *oldimg = n->getValue();
if(oldimg)
oldimg->drop();
}
// Try to use local texture instead if asked to
if(prefer_local){
std::string path = getTexturePath(name.c_str());
if(path != ""){
video::IImage *img2 = driver->createImageFromFile(path.c_str());
if(img2){
m_images[name] = img2;
return;
}
}
}
img->grab();
m_images[name] = img;
}
video::IImage* get(const std::string &name)
{
core::map<std::string, video::IImage*>::Node *n;
n = m_images.find(name);
if(n)
return n->getValue();
return NULL;
}
// Primarily fetches from cache, secondarily tries to read from filesystem
video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
{
core::map<std::string, video::IImage*>::Node *n;
n = m_images.find(name);
if(n){
n->getValue()->grab(); // Grab for caller
return n->getValue();
}
video::IVideoDriver* driver = device->getVideoDriver();
std::string path = getTexturePath(name.c_str());
if(path == ""){
|